この記事では、JavaScriptを活用して、コードブロック内のテキストを折り返し可能にし、かつコピー機能を実装する手法に焦点を当てています。フロントエンドデザインにおいて有益なテクニックを紹介し、具体的なコーディング方法も解説していきます。
今回ご紹介するのは、preタグを使ったコード表記に折り返しとコピーボタンを生成するスクリプトです。
Markdown
```markdown ここにコードを書く ```
はてな記法(はてなブログのみ)
>|hatena| ここにコードを書く ||<
Markdownやはてな記法で上記のように記述すると、以下のようなHTML構造が生成されます。
HTML
<pre class="code lang-markdown" data-lang="markdown" data-unlink=""> ここにコードを書く </pre>
このように生成された"code"というクラスを持つpreタグ( <pre class="code">
)をJavaScriptコードで検出し、HTML構造に要素を追加し、コードの折り返し・コピー機能を実装します。
デモページ
デモページはこちらです。
コードと解説
①head内にCSSコードを追加
document.addEventListener( "DOMContentLoaded", function() { var e = document.createElement( "style" ); e.innerHTML = '.entry-content pre.code{white-space: pre}.entry-content div.code-box a{text-decoration: unset}'; document.getElementsByTagName( "head" )[ 0 ].appendChild( e ); } );
このコードはhead内に動的にCSSスタイルを追加するスクリプトです。以下、詳細な解説です。
document.addEventListener("DOMContentLoaded", function () { ... });
:- ページがDOM(Document Object Model)の構築を完了した時点で発生する
DOMContentLoaded
イベントに対するリスナーを追加しています。 - これにより、ページが読み込まれた後にコードが実行されます。
スクリプトの実行タイミングが、DOMが完全に読み込まれて解析された後になるため、挙動が安定します。
- ページがDOM(Document Object Model)の構築を完了した時点で発生する
var e = document.createElement("style");
:- 新しい
style
要素を作成しています。この要素は、CSSスタイルを動的に追加するために使用されます。
- 新しい
e.innerHTML = '.entry-content pre{white-space: pre}.entry-content a {text-decoration: unset}';
:style
要素のinnerHTML
プロパティを設定して、特定のCSSスタイルルールを追加しています。- このルールは、
pre.code
(codeクラスを持つpreタグ)とdiv.code-box a
(code-boxクラスを持つdivタグ内の「a」タグ)というセレクタに対して、それぞれwhite-space: pre
(コードブロック内の空白を維持する)およびtext-decoration: unset
(リンクの下線を取り除く)のスタイルを適用します。 white-space: pre
の指定によりそのpreタグ内は、- HTML内の改行や半角スペースはブラウザ表示にそのまま反映され、
- 改行のない行は自動では折り返されません
これを初期の値とし、のちのコードでテキストエリアの折り返しON/OFFを切り替えます。
document.getElementsByTagName("head")[0].appendChild(e);
:head
要素を取得し、appendChild
メソッドを使用して新しく作成したstyle
要素をhead
要素に追加しています。- これにより、ページが読み込まれたときに動的にCSSスタイルが追加されます。
総じてこのコードは、ページが読み込まれた時点で指定したCSSスタイルを.entry-content pre.code
と.entry-content div.code-box a
に適用します。
②preタグにHTMLコードを追加
const codeElements = document.querySelectorAll( 'pre.code' ); codeElements.forEach( ( codeElement ) => { const codeContainer = document.createElement( 'div' ); codeContainer.className = 'code-box'; const copyButton = document.createElement( 'button' ); copyButton.className = 'code-copy-btn'; copyButton.textContent = 'Copy'; codeElement.parentNode.insertBefore( codeContainer, codeElement ); codeContainer.appendChild( codeElement ); codeContainer.appendChild( copyButton ) } );
このコードは、
- "code" というクラスの付いた
pre
タグを取得して <div class="code-box">
で囲み、</pre>
タグの直後には<button class="code-copy-btn">
というボタン要素を追加する
スクリプトです。以下、詳細な解説です。
**
const codeElements = document.querySelectorAll('pre.code');**
:pre
要素でクラスが "code" となっているものをすべて取得します。
codeElements.forEach((codeElement) => { ... });
:- 取得した各コードブロックに対して、以下の処理を繰り返します。
余談ですが「For Each」はVBA(マクロ)でも繰り返しの構文です。
- 取得した各コードブロックに対して、以下の処理を繰り返します。
const codeContainer = document.createElement('div');
:- 新しい
div
要素を作成し、これをコードブロックを包むコンテナとして使用します。
- 新しい
codeContainer.className = 'code-box';
:- 作成した
div
要素に "code-box" というクラスを追加します。これは後でスタイリングや識別のために使用されます。
- 作成した
const copyButton = document.createElement('button');
:- 新しい
button
要素を作成します。これはコピー機能を提供するボタンとして使用されます。
- 新しい
copyButton.className = 'code-copy-btn';
:- 作成したボタンに "code-copy-btn" というクラスを追加します。これもスタイリングや識別のためです。
copyButton.textContent = 'Copy';
:- ボタンの表示テキストを 'Copy' に設定します。
codeElement.parentNode.insertBefore(codeContainer, codeElement);
- 新しく作成したコードコンテナ(
<div class="code-box">
)をコードブロックの親要素(<pre>
~</pre>
)の前に挿入します。
- 新しく作成したコードコンテナ(
codeContainer.appendChild(codeElement);
:<div class="code-box">
~</div>
内に元のコードブロック(<pre>
~</pre>
)を移動させます。これにより、コードブロックが新しく作成されたコンテナ内に収められます。
codeContainer.appendChild(copyButton);
:- コードコンテナ内に作成したコピー用ボタンを追加します。
総じてこのコードは、各コードブロックに対して新しいコンテナとコピー機能を追加し、ページ上でコードの表示とコピーが簡単になるようにします。この時点ではコピー機能は未実装です。
③コードブロックに折り返し機能を追加
var preElements = document.querySelectorAll( ".code-box pre" ); preElements.forEach( function( e ) { if ( e.scrollWidth > e.clientWidth ) { var t = document.createElement( "div" ); t.className = "code-box", t.innerHTML = '<a href="#" onclick="return false;" style="display: flex; align-items: center;"><i class="fa-solid fa-toggle-off"></i>折り返しON', e.parentNode.insertBefore( t, e.parentNode.firstChild ); var n = t.querySelector( "a" ); t.addEventListener( "click", function() { return "auto" === e.style.overflowX ? ( e.style.overflowX = "visible", e.style.whiteSpace = "pre", n.innerHTML = '<i class="fa-solid fa-toggle-off"></i>折り返しON' ) : ( e.style.overflowX = "auto", e.style.whiteSpace = "pre-wrap", n.innerHTML = '<i class="fa-solid fa-toggle-on"></i>折り返しOFF' ), !1 } ) } } );
このコードは、
.code-box pre
を持つコードブロックが- 水平方向にスクロールバーを持っているかどうかを確認し、
- スクロールバーがある場合に
- 折り返し機能をトグルするリンクを追加する
スクリプトです。以下、詳細な解説です。
①コードブロックの取得:
var preElements = document.querySelectorAll(".code-box pre");
-
.code-box pre
クラスを持つすべてのpre
要素を取得します。
②各コードブロックに対する処理:
preElements.forEach(function (e) {...});
- 各コードブロックに対して以下に続く処理を繰り返します。
③水平スクロールバーの有無を確認:
if (e.scrollWidth > e.clientWidth) {...}
- もしコードブロックの横幅が表示領域の横幅よりも大きい場合(=水平スクロールバーが存在する場合)、以下に続く処理を実行します。
④UI要素の作成と追加:
var t = document.createElement("div"); t.className = "code-box", t.innerHTML = '<a href="#" onclick="return false;" style="display: flex; align-items: center;"><i class="fa-solid fa-toggle-off"></i>折り返しON', e.parentNode.insertBefore(t, e.parentNode.firstChild);
- 新しい
div
要素(t
)を作成し、これに対してクラス(code-box
)やHTML(<a href="#" onclick="return false;" style="display: flex; align-items: center;"><i class="fa-solid fa-toggle-off"></i>折り返しON
)を設定します。 - その後、作成した
div
要素をコードブロックの親要素の最初の子要素(firstChild
)として追加します。
⑤UI要素内の要素の取得:
var n = t.querySelector("a");
- 作成した
div
要素内のa
要素を取得します。
⑥クリックイベントリスナーの追加:
t.addEventListener("click", function() {...});
-
div
要素(UI要素)にクリックイベントリスナーを追加します。クリックイベントリスナーとは、ボタンやリンクなどがクリックされたときに実行される関数です。
⑦折り返し機能のトグル:
return "auto" === e.style.overflowX ? ( e.style.overflowX = "visible", e.style.whiteSpace = "pre", n.innerHTML = '<i class="fa-solid fa-toggle-off"></i>折り返しON' ) : ( e.style.overflowX = "auto", e.style.whiteSpace = "pre-wrap", n.innerHTML = '<i class="fa-solid fa-toggle-on"></i>折り返しOFF' ), !1
- クリック時に、コードブロックの
overflowX
(横方向のオーバーフロー設定)およびwhiteSpace
(空白の取り扱い設定)をトグルします。 - これにより、折り返しON/OFFが切り替わります。
クリック時に
false
を返す( ④onclick="return false"
)ことで、親要素に対するクリックイベントが発生しないようにしています。
総じてこのコードは、コードブロックに水平スクロールバーが存在する場合に、ユーザーがクリックすることで折り返し機能をON/OFFできるUIを追加しています。
④コードブロックにコピー機能を追加
var copyButtons = document.querySelectorAll( ".code-copy-btn" ); copyButtons.forEach( function( e ) { e.addEventListener( "click", function() { var t = e.previousElementSibling.textContent , n = document.createElement( "textarea" ); n.value = t, document.body.appendChild( n ), n.select(), document.execCommand( "copy" ), document.body.removeChild( n ), e.textContent = "Copied!", setTimeout( function() { e.textContent = "Copy" }, 2e3 ) } ) } );
このコードは、ボタンがクリックされたときにテキストをクリップボードにコピーするスクリプトです。以下、詳細な解説です。
querySelectorAll(".code-copy-btn")
:- クラスが "code-copy-btn" であるすべての要素(ボタン)を取得します。
copyButtons.forEach( function( e )
:- 各ボタンに対し繰り返しメソッドでクリックイベントリスナー(
addEventListener( "click", function()
)を追加します。
- 各ボタンに対し繰り返しメソッドでクリックイベントリスナー(
previousElementSibling
:- クリック時に、クリックされたボタン(
button
)の前にある要素(previousElementSibling
)を取得します。
- クリック時に、クリックされたボタン(
createElement( "textarea" )
:- 取得したテキストをコピーするために、一時的なテキストエリアを作成し、その中にテキストを設定(
n.value = t
)します。
- 取得したテキストをコピーするために、一時的なテキストエリアを作成し、その中にテキストを設定(
document.execCommand("copy")
:- テキストエリア内のテキストをクリップボードにコピーします。
document.body.removeChild( n )
:- テキストエリア(
n
)をページ(document.body
)から削除(.removeChild(n)
)します。
- テキストエリア(
e.textContent = "Copied!"
:- ボタンの表示を "Copied!" に変更して、ユーザーにコピーが完了したことを示します。
setTimeout( function() { e.textContent = "Copy" }, 2e3
- 一定時間(ここでは2秒)が経過した後に、ボタンの表示を "Copy" に戻します。
2e3
は指数表記で、2 * 10^3
(10の3乗)と解釈されます。つまり、2e3
は 2 * 1000 と同じで、2e3
は2秒(1秒=1000ミリ秒)を表します。
- 一定時間(ここでは2秒)が経過した後に、ボタンの表示を "Copy" に戻します。
総じてこのコードは、ボタンがクリックされるとそのボタンの前にあるテキストがクリップボードにコピーされ、ボタンの表示も一時的に変更されます。
minify化したまとめコード
解説した4つのJSコードをまとめました。ご自身のブログで利用する場合、<script>
~</script>
タグでコードを囲み、フッタや記事下などに貼り付けてください。
クリックでコピー
Font Awesome
コードでは、切り替えボタンにFont Awesomeを利用していますが、これははてなアイコンでも同様のアイコンがあり、代用可能です。
Icon | HTML Code | Icon Name |
---|---|---|
<i class="fa-solid fa-toggle-on"></i> |
Font Awesome | |
<i class="blogicon-toggle-on"></i> |
Hatena | |
<i class="fa-solid fa-toggle-off"></i> |
Font Awesome | |
<i class="blogicon-toggle-off"></i> |
Hatena |
Font Awesomeをまだ設定していない場合、以下のコードを<head>要素もしくは記事内に追加することで、Font Awesomeが利用可能になります。
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css"/>
Font Awesomeを利用したくない、という場合は以下のコードを利用してください。
クリックでコピー(はてなアイコン仕様)
おわりに
コードブロックの使いやすさを向上させるためのテクニックを学びました。折り返し機能やコピー機能の実装は、読者の利便性を高めるだけでなく、ブログ全体のユーザーエクスペリエンスを向上させる重要なステップです。
これらの手法を活用して、より魅力的で使いやすいブログを構築しましょう~