ソースコードシンタックスハイライトに対応した前回の記事の続きで、今回はコードをクリップボードにコピーする機能を追加します。
ちょうどいいパッケージを探したのですが、見つかったパッケージは長らくメンテされてないようで、試したのですが動きませんでした。ほかに見つからなかったので、結局フルスクラッチで書きました。
HTMLの中で $md.render(content)
と書いていたところを renderMarkdown(content)
に置き換えて、 renderMarkdown
を独自実装することで対応させます。
<div class="markdown" v-html="renderMarkdown(content)"></div>
Markdownで生成したDOM要素に対して、クリックイベントなどを拾えるようにする初期設定を renderMarkdown
の中から呼び出します。
methods: {
renderMarkdown(text) {
setTimeout(() => {
this.initializeMarkdownDOM();
}, 10);
return this.$md.render(text);
},
initializeMarkdownDOM() {
document.querySelectorAll('.markdown pre').forEach((preElement) => {
if (preElement.dataset.initialized) {
return;
}
preElement.dataset.initialized = "true";
this.initializeMarkdownCodeBlock(preElement);
});
},
initializeMarkdownCodeBlock(preElement) {
const buttonAreaElem = document.createElement("div");
buttonAreaElem.classList.add("btn-area");
buttonAreaElem.style.display = "none";
preElement.insertAdjacentElement("afterbegin", buttonAreaElem);
const copyButton = document.createElement("a");
copyButton.innerHTML = '<i aria-hidden="true" class="v-icon notranslate mdi mdi-content-copy theme--dark primary--text"></i>';
copyButton.href = "#";
copyButton.addEventListener("click", (event) => {
event.preventDefault();
const codeElem = preElement.querySelector("code");
const text = codeElem.innerText;
this.copyToClipboard(text);
});
buttonAreaElem.insertAdjacentElement("beforeend", copyButton);
preElement.addEventListener("mouseenter", () => {
buttonAreaElem.style.display = "block";
});
preElement.addEventListener("mouseleave", () => {
buttonAreaElem.style.display = "none";
});
},
copyToClipboard(text) {
navigator.clipboard.writeText(text);
},
},
CSSには以下を加えます。これはコピーボタンを <pre>
エリアの右上に配置するための記述です。
.markdown pre {
position: relative;
}
.markdown pre .btn-area {
position: absolute;
box-sizing: border-box;
width: 100%;
padding-right: 10px;
text-align: right;
}
これで、コード上にマウスカーソルを乗せたときに右上にコピーボタンが現れ、クリックするとクリップボードにコードがコピーされます。