在代码pre标签前面插入复制按钮
js代码,这里比较简单直接在pre标签前插入一个code-tool-container,新增悬停显示复制按钮:
(function () {
"use strict"
//在这里使用需要判断样式是否不存在,不存在则开始添加到head中,避免了使用return导致函数出现终止执行问题
if (!document.getElementById("copy-code-styles")) {
//这里就是添加css样式表到head的主要代码
const css =
`
/* 设置容器元素样式,使其定位为相对定位(position: relative) */
.code-tool-container {
position: relative;
}
.code-tool-container.ctc-hidden {
display: none;
}
.copy-btn {
font-size: 13px;
transition: color 0.1s;
color: hsl(9.96deg 88.32% 47.38% / 97%);
background: #dfe8e6;
padding: 0 3px;
border: none;
border-radius: 4px;
cursor: pointer;
z-index: 1;
}
`;
const style = document.createElement("style");
style.id = "copy-code-styles";
style.textContent = css;
document.head.appendChild(style);
}
// 获取<pre>元素
const codeBlocks = document.querySelectorAll('pre');
const toolContainers = document.querySelectorAll(".code-tool-container");
if (codeBlocks) {
codeBlocks.forEach(function (codeBlock, cbIndex) {
//这段注释的代码是给每一行添加行数,便于代码阅读和显示
//判断当前代码块不存在行数显示标签ol时,才创建新的ol
// if (!codeBlock.querySelectorAll('ol')) {
// // 创建新的ol元素
// const ol = document.createElement('ol');
// // 获取所有<code>标签中的文本行
// const codeLines = codeBlock.textContent.split('\n');
// // 移除<pre>中的所有内容
// codeBlock.innerHTML = '';
// // 为每行代码添加序号并重新添加到<pre>中
// codeLines.forEach((line, index) => {
// //const lineNumber = index + 1;
// const lineElement = document.createElement('li');
// lineElement.textContent = `${line}`;//${lineNumber}.
// ol.appendChild(lineElement);
// });
// codeBlock.appendChild(ol);
// }
//判断当前代码块的索引序号是否小于或等于复制按钮容器数,为true表示没有新的代码块显示,返回终止函数
if (toolContainers && cbIndex + 1 <= toolContainers.length) {
return;
}
var copyButton = document.createElement('span');
copyButton.className = 'copy-btn';
copyButton.textContent = '复制';
// 创建包裹代码块和按钮的容器元素
var toolContainer = document.createElement('div');
toolContainer.className = 'code-tool-container ctc-hidden';
// 将按钮添加到容器元素内
toolContainer.appendChild(copyButton);
// 将容器元素插入到代码块之前
codeBlock.parentNode.insertBefore(toolContainer, codeBlock);
// 设置复制按钮样式,使其绝对定位于容器元素的右上角
copyButton.style.position = 'absolute';
copyButton.style.top = '3px';
copyButton.style.right = '6px';
copyButton.addEventListener('click', function () {
// 获取代码块的文本内容textContent
var code = codeBlock.innerText;
if (navigator.clipboard && window.isSecureContext) {
try {
navigator.clipboard.writeText(code).then(() => {
// 修改复制按钮文本为“已复制”
this.textContent = '复制成功';
}).catch(() => {
this.textContent = '复制失败';
});
} catch (err) {
this.textContent = '复制失败';
}
} else {
// 创建一个临时的textarea元素,并将代码块的内容设置为其值
var textarea = document.createElement('textarea');
textarea.value = code;
// 将textarea元素追加到body中
document.body.appendChild(textarea);
// 选中textarea中的文本
textarea.select();
// 执行复制操作
document.execCommand('copy');
// 移除临时的textarea元素
document.body.removeChild(textarea);
this.textContent = '复制成功';
}
//一定时间后把按钮名改回来
setTimeout(() => {
this.textContent = "复制";
}, 1800);
});
//实现代码块pre悬停显示复制按钮等,包括桌面和移动端
// 监听mouseenter和mouseleave事件
codeBlock.addEventListener('mouseenter', (e) => {
// 鼠标进入元素时的效果
toolContainer.classList.remove('ctc-hidden');
toolContainer.addEventListener("mouseenter", (e) => {
e.target.classList.remove('ctc-hidden');
})
});
codeBlock.addEventListener('mouseleave', (e) => {
// 鼠标离开元素时的效果
toolContainer.classList.add('ctc-hidden');
toolContainer.addEventListener("mouseleave", (e) => {
e.target.classList.add('ctc-hidden');
})
});
// 为了适配触摸设备,可以监听触摸事件
codeBlock.parentElement.addEventListener('touchstart', function (e) {
// 阻止默认的触摸行为,例如缩放
//e.preventDefault();
if (codeBlock.contains(e.target) || toolContainer.contains(e.target)) {
// 触摸开始时的效果
toolContainer.classList.remove('ctc-hidden');
}
});
// 为了适配触摸设备,可以监听页面,当任意地方按下或触摸时
document.addEventListener("touchstart", (e) => {
// 任意点击不是当前pre的位置
if (!codeBlock.contains(e.target) && !toolContainer.contains(e.target)) {
toolContainer.classList.add('ctc-hidden');
}
});
});
}
// if (document.getElementById("copy-code-styles")) return; // 避免重复添加样式
//如果要使用这种方法,需要把上面的判断样式不存在中的添加css的代码转移到下面,
//因为return;会终止当前函数,也就是下面的不会再执行了,所以添加到head的方法移到下面
}());
在代码块pre中添加一个容器,里面包含复制按钮
这里是在pre中添加一个容器,里面放置一键复制按钮,主要可以实现更多的定制化,对于一键复制和其他功能可以更加自由扩展,需要在pre上添加position: relative;
Jаvascript
为代码块添加一个"复制代码"按钮,并实现点击按钮后将代码块的内容复制到剪贴板中。
- 首先通过
document.querySelectorAll('pre')
获取所有<pre>
元素(即代码块)。- 使用
forEach
方法遍历每个代码块。- 创建一个文本元素
copyButton
,设置其class为"copy",并将显示文本设置为"复制代码"。- 创建一个容器元素
container
,设置其class为"code-container",并将复制按钮添加到容器元素内。- 将容器元素插入到代码块之前。
- 设置容器元素样式,使其定位为相对定位(position: relative)。
- 设置复制按钮样式,使其绝对定位于容器元素的右上角。
- 为复制按钮添加点击事件监听器。
- 在点击事件处理函数中,获取代码块的文本内容。
- if (navigator.clipboard && window.isSecureContext)以此判断使用不同的复制方法,使用navigator.clipboard.writeText(code).then(() => {}).catch(() => {});或采用document.execCommand('copy'); 这里需要借助创建textarea
- 创建一个临时的
<textarea>
元素,并将代码块的内容设置为其值。- 将
<textarea>
元素追加到<body>
中。- 选中
<textarea>
中的文本。- 执行复制操作,将选中的文本复制到剪贴板中。
- 移除临时的
<textarea>
元素。- 修改复制按钮文本为"复制成功"。
这段代码的作用是为网页中的代码块添加一个复制按钮,方便复制代码片段。
CSS
用于设置复制按钮和代码块的样式。具体样式如下:
.code-wrapper
类选择器用于设置包裹代码块的容器元素的样式。在这里设置了相对定位(position: relative)。.code-block
类选择器用于设置代码块的样式。在这里设置了相对定位(position: relative)。.copy
类选择器用于设置复制按钮的样式。具体样式如下:
font-size
:设置字体大小为 13px。transition
:设置颜色变化的过渡效果为 0.1秒。color
:设置按钮的颜色为带透明度的红色color: hsl(165.03deg 19.14% 92.37% / 97%);,背景为灰色background: #1b45bede;。border
:去掉按钮的边框。border-radius
:设置按钮的圆角为4px。cursor
:设置鼠标悬停在按钮上时的样式为指针。z-index
:将复制按钮的层级置于顶层,确保按钮显示在其他内容之上。
这些样式可以使用在前面提到的 jаvascript 脚本中的相关元素上,以实现更好的外观和交互效果。
js文件,直接在html中引用 ,如果是在vue中使用,需要在渲染html的方法中使用下面的js。
(function () {
//在这里使用需要判断样式是否不存在,不存在则开始添加到head中,避免了使用return导致函数出现终止执行问题
if (!document.getElementById("copy-code-styles")) {
//这里就是添加css样式表到head的主要代码
const css = `
.code-wrapper {
position: relative;
}
.code-block {
position: relative;
}
.copy {
font-size: 13px;
transition: color 0.1s;
color: hsl(9.96deg 88.32% 47.38% / 97%);
background: #dfe8e6;
padding: 0 3px;
border: none;
border-radius: 4px;
cursor: pointer;
z-index: 1;
}
`;
const style = document.createElement("style");
style.id = "copy-code-styles";
style.textContent = css;
document.head.appendChild(style);
}
// 获取<pre>元素
const codeBlocks = document.querySelectorAll('pre');
//获取复制按钮所在容器
const codeContainer = document.querySelectorAll(".code-container");
if (codeBlocks) {
codeBlocks.forEach(function (codeBlock) {
//这段注释的代码是给每一行添加行数,便于阅读
//判断当前代码块不存在行数显示标签ol时,才创建新的ol
// if (!codeBlock.querySelectorAll('ol')) {
// // 创建新的ol元素
// const ol = document.createElement('ol');
// // 获取所有<code>标签中的文本行
// const codeLines = codeBlock.textContent.split('\n');
// // 移除<pre>中的所有内容
// codeBlock.innerHTML = '';
// // 为每行代码添加序号并重新添加到<pre>中
// codeLines.forEach((line, index) => {
// //const lineNumber = index + 1;
// const lineElement = document.createElement('li');
// lineElement.textContent = `${line}`;//${lineNumber}.
// ol.appendChild(lineElement);
// });
// codeBlock.appendChild(ol);
// }
//判断当前代码块的索引序号是否小于或等于复制按钮容器数,为true表示没有新的代码块显示,返回终止函数
//这里使用的原因是我的应用场景涉及到html动态变化,会出现新增的代码块
//一般情况是在文章页面加载完成调用一次,而文章显示页面不会出现动态修改的问题,所以可以不用使用这个判断条件
if (codeContainer && cbIndex+1 <= codeContainer.length) {
return;
}
var copyButton = document.createElement('span');
copyButton.className = 'copy';
copyButton.textContent = '复制代码';
// 创建包裹代码块和按钮的容器元素
var container = document.createElement('div');
container.className = 'code-container';
// 将按钮添加到容器元素内
container.appendChild(copyButton);
// 将容器元素插入到代码块之前
codeBlock.parentNode.insertBefore(container, codeBlock);
// 设置容器元素样式,使其定位为相对定位(position: relative)
container.style.position = 'relative';
// 设置复制按钮样式,使其绝对定位于容器元素的右上角
copyButton.style.position = 'absolute';
copyButton.style.top = '3px';
copyButton.style.right = '6px';
copyButton.addEventListener('click', function () {
// 获取代码块的文本内容textContent
var code = codeBlock.innerText;
if (navigator.clipboard && window.isSecureContext) {
try {
navigator.clipboard.writeText(code).then(() => {
// 修改复制按钮文本为“已复制”
this.textContent = '复制成功';
}).catch(() => {
this.textContent = '复制失败';
});
} catch (err) {
this.textContent = '复制失败';
}
} else {
// 创建一个临时的textarea元素,并将代码块的内容设置为其值
var textarea = document.createElement('textarea');
textarea.value = code;
// 将textarea元素追加到body中
document.body.appendChild(textarea);
// 选中textarea中的文本
textarea.select();
// 执行复制操作
document.execCommand('copy');
// 移除临时的textarea元素
document.body.removeChild(textarea);
this.textContent = '复制成功';
}
//一定时间后把按钮名改回来
setTimeout(() => {
this.textContent = "复制代码";
}, 1800);
});
});
}
}());