إضافة زر نسخ الكود في صفحة ويب


سنتطرق في هذه التدوينة إلى طريقة إضافة زر نسخ الشيفرة "code" إلى حاوية الأكواد "Code snippets" في صفحة ويب، سواءً كانت واحدة أم أكثر بأبسط طريقة من خلال جافاسكربت و كذلك رياكت جي اس "Reactjs".
ملاحظة : الترجمة الحرفية لـ "Code snippets" هي مقتطفات الشيفرة، و لكني فضّلت تسميتها بحاوية أكواد لأنها تشبه الحاوية و ربما صندوق الشيفرة أقرب للمعنى الظاهر لها.
إضافة الزر من خلال Vanilla JS
في البداية لنضع الكود :
const addCopyButton = () => {
const pre = document.querySelectorAll("pre");
const copy = '<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 448 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z"></path></svg>';
const copied = '<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 512 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z"></path></svg>';
pre.forEach(e => {
e.classList.add("relative");
const copyButton = document.createElement("button");
copyButton.innerHTML = `${copy}`;
copyButton.setAttribute(
"class",
"absolute top-1 z-[10000] right-1 bg-neutral-600 px-2 py-2 text-sm"
);
copyButton.addEventListener("click", async () => {
try {
await navigator.clipboard.writeText(e.innerText.trim());
copyButton.innerHTML = `${copied}`;
} catch (err) { }
setTimeout(() => {
copyButton.innerHTML = `${copy}`;
}, 700);
})
e.append(copyButton);
})
}
addCopyButton();
شرح الكود
في البداية نعرّف دالة ( Function ) جديدة بأيّ اسم. و في مثالنا أعطيناها اسم addCopyButton
.
في سطر الأول const pre ...
نعرّف متغير لنضع فيه كافة عناصر الوسم pre
من خلال إستعلام محدّد querySeletorAll
عن الوسم ( tag ) pre
.
في السطر الثاني و الثالث نعرّف متغيرين copy
- copied
يحملون قيمة لأيقونة svg
تعبّران عن أيقونتيّ "النسخ و علامة الصح" .
في السطر الرابع نبدأ بعملية المرور على كافة العناصر من خلال "forEach".
في السطر الخامس نضيف كلاس ( class ) relative
و الذي تكون قيمته ب css position: relative
و سنشرح الهدف منه لاحقًا.
السطر السادس و السابع نعرّف متغير لزر النسخ و ننشئ زر html من خلال createElement
و نضيف له أيقونة النسخ.
السطر الثامن إلى الحادي العشر نضيف خاصية ( attribute ) class و قيمتها كلاسات من مكتبة tailwindcss و التي تعادل أكواد في css :
position : absolute;
top : 4px;
z-index: 10000;
right : 4px;
padding: 8px;
text-size: 14px;
background-color: #525252;
كود css السابق يثبّت العنصر في مكان المحدد و هو فالأعلى و اليمين مع إضافة حجم للخط لضبط حجم أيقونات
svg
و لون الخلفية يُناسب الحاوية و z-index بقيمة مُرتفعة لوجود بعض مكتبات highlight تستخدم نظام الطبقات و في هذه الحالة نضمن ظهور الزر فوق الصندوق، و تم إضافةrelative
لوسمpre
من أجل إعلام الزر بأن حدوده هو الوسمpre
و ليسbody
.
في السطر الثاني عشر ننشئ تنصّت "Listener" على ضغطة "click" من خلال addEventListener
لزر النسخ
في السطر الثالث عشر إلى السادس عشر عبارة عن شيفرة لنسخ ما بداخل الحاوية من خلال navigator.clipboard.writeText
و المنسوخ هو e.innerText
و الذي يكون النص الذي بداخل وسم pre
.
في السطر السابع عشر إلى التاسع عشر نستخدم setTimeout
و الهدف هو إعادة أيقونة النسخ لزر النسخ بعد تغييرها أثناء "click" لإضفاء تأثير.
في النهاية نضيف زر النسخ إلى داخل وسم pre بعد ضبطه.
تبقّت خطوة واحدة و هي إستدعاء الدالة لتتنفذ و هو السطر الأخير من الكود.
ملاحظة : يفضّل إضافة الكود في نهاية الصفحة عند تقفيلة وسم
body
لضمان إن الكود يتنفّذ بعد عمليةrender
للصفحة.
إضافة الزر من خلال Reactjs components | Nextjs
من أجل تشغيل الكود في reactjs component أو nextjs نحتاج إلى تعديل بضعة أمور للكود السابق :
نستعمل إحدى hooks التي توفّرها لنا reactjs و هي useEffect
و نضيف شرط بسيط و هو :
if (document) addCopyButton();
ليصبح كود useEffect :
useEffect(() => {
if (document) addCopyButton();
}, []);
و لذلك لضمان بأن الدالة تعمل بعد عملية render لـ component.
كود Reactjs Typescript
const addCopyButton = () => {
const pre: NodeListOf<HTMLPreElement> = document.querySelectorAll("pre");
const copy: string = '<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 448 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z"></path></svg>';
const copied: string = '<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 512 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z"></path></svg>';
pre.forEach((e) => {
e.classList.add("relative");
const copyButton: HTMLButtonElement = document.createElement("button");
copyButton.innerHTML = `${copy}`;
copyButton.setAttribute(
"class",
"absolute top-1 z-[10000] right-1 bg-neutral-600 px-2 py-2 text-sm"
);
copyButton.addEventListener("click", async () => {
try {
await navigator.clipboard.writeText(e.innerText);
copyButton.innerHTML = `${copied}`;
} catch (err) { }
setTimeout(() => {
copyButton.innerHTML = `${copy}`;
}, 700);
});
e.append(copyButton);
});
}
useEffect(() => {
if (document) addCopyButton();
}, []);
كود Reactjs Javascript
const addCopyButton = () => {
const pre = document.querySelectorAll("pre")
const copy =
'<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 448 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z"></path></svg>'
const copied =
'<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 512 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z"></path></svg>'
pre.forEach(e => {
e.classList.add("relative")
const copyButton = document.createElement("button")
copyButton.innerHTML = `${copy}`
copyButton.setAttribute(
"class",
"absolute top-1 z-[10000] right-1 bg-neutral-600 px-2 py-2 text-sm"
)
copyButton.addEventListener("click", async () => {
try {
await navigator.clipboard.writeText(e.innerText)
copyButton.innerHTML = `${copied}`
} catch (err) {}
setTimeout(() => {
copyButton.innerHTML = `${copy}`
}, 700)
})
e.append(copyButton)
})
}
useEffect(() => {
if (document) addCopyButton()
}, [])
ملاحظة : الكود السابق يعمل على Nextjs و إذا كُنت تستخدم نسخة 13 فأعلى فتأكد بأن component معلّم عليه بـ
'use client'
.
مراجع ستفيدك
للإستزادة في موضوع hooks يمكن قراءة التدوينة من مدونة tutomena شرح React Hooks.. الميزة الجديدة القادمة لِ React.js.