Go to list of users who liked
テキストをコピペするときにスタイルごとコピーされちゃうのってどんな仕組み?
概要
文章をコピペしてエクセルに張り付けたときに、画面のスタイルもコピーされてしまって困ったことはありますか?ありますよね!
私もよくやってしまうのですが、実際にどのような処理が行われているのかよく分かっていませんでした。理解を深めるためにも、自分で実装して謎を解いていきたいと思います。
3つパターンの処理を実装
比較のため、プレーンテキスト・HTMLテキスト・リッチテキストのコピー機能をサンプルプログラムを実装してみました。
(リッチテキストのコピーが、範囲選択してコピペしたときと同じ機能を想定しています。)
HTMLファイル
画面表示されるHTMLは下記のような感じです。各コピー処理でid="message"
の部分を固定でコピーするようにします。
<!DOCTYPE html><htmllang="ja"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>コピーサンプル</title></head><body><divid="message"><divstyle="color:red; font-size: 20px; font-weight: bold;">トマト</div><divstyle="color:green; font-size: 24px; font-weight: bold;">ピーマン</div></div><divid="buttons"><buttononclick="copyPlainText()">プレーンテキストコピー</button><buttononclick="copyHTML()">HTMLコピー</button><buttononclick="copyRichText()">リッチテキストコピー</button></div></body></html>
1. プレーンテキストをコピーする機能
プレーンテキストをコピーするには、innerText
を使ってテキスト取得します。
// プレーンテキストをコピーfunctioncopyPlainText(){consttextContent=document.getElementById("message").innerText;navigator.clipboard.writeText(textContent);}
動作はこんな感じになります。ストレスないコピペライフが送れそうです。
2. HTMLコンテンツをコピーする機能
HTMLコンテンツをコピーするには、innerHTML
を使います。
// HTMLコンテンツをコピーfunctioncopyHTML(){consthtmlContent=document.getElementById("message").innerHTML;navigator.clipboard.writeText(htmlContent);}
動作はこんな感じになり、対象のHTMLタグの中身がテキストで取得できました。
3. リッチテキストをコピーする機能
さて、本題です。
リッチテキストをコピーするには、innerText
とinnerHTML
の両方使うようです。ClipboardItem
に定義するとスタイルを維持したままコピーされるみたいです。
// リッチテキストをコピーfunctioncopyRichText(){constrichContent=document.getElementById("message");navigator.clipboard.write([newClipboardItem({"text/plain":newBlob([richContent.innerText],{type:"text/plain"}),"text/html":newBlob([richContent.innerHTML],{type:"text/html"})})]);}
動作はこんな感じです。ちゃんとスタイルが維持されてコピーできました。
問題が発覚
実はinnerHTML
だと直接範囲選択したときと挙動が違います。
以下のようにインラインでのスタイル指定をやめてみます。
<!DOCTYPE html><htmllang="ja"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>コピーサンプル</title><scripttype="text/javascript"src="./copy.js"></script><style>.tomato{color:red;font-size:20px;font-weight:bold;}</style></head><body><divid="message"><divclass="tomato">トマト</div><divstyle="color:green; font-size: 24px; font-weight: bold;">ピーマン</div></div><divid="buttons"><buttononclick="copyPlainText()">プレーンテキストコピー</button><buttononclick="copyHTML()">HTMLコピー</button><buttononclick="copyRichText()">リッチテキストコピー</button></div></body></html>
実装した機能でコピペした場合
インラインで定義されていない部分のスタイルがコピペできていません。
範囲選択してコピペした場合
実装した機能を使わず範囲選択でコピペしたときは、正しくスタイルごとコピペできています。
同じ挙動をするように直していきたいと思います。
プログラム修正
document.styleSheets
で適応されているスタイルを取得し、それを利用することでスタイルを維持するように修正しました。
functioncopyRichText(){constrichContent=document.getElementById("message");// コピーするコンテンツを複製constclone=richContent.cloneNode(true);// スタイルを抽出して適用conststyleSheets=Array.from(document.styleSheets);letstyleContent="";styleSheets.forEach(sheet=>{construles=Array.from(sheet.cssRules||[]);rules.forEach(rule=>{styleContent+=rule.cssText+"\n";});});// 抽出したスタイルを<style>タグに追加conststyleTag=document.createElement("style");styleTag.textContent=styleContent;// 新しいHTML構造を作成constcontainer=document.createElement("div");container.appendChild(styleTag);container.appendChild(clone);// HTMLコンテンツをコピーnavigator.clipboard.write([newClipboardItem({"text/plain":newBlob([richContent.innerText],{type:"text/plain"}),"text/html":newBlob([container.innerHTML],{type:"text/html"})})]);}
実際の動作です。範囲選択してコピペしたときと同じ動きにすることができました。
(範囲選択してコピペしたときに、この処理をしているかは不明です。)
まとめ
実際にリッチテキストのコピーを含めた3つの機能を実装してみました。
これでリッチテキストの謎が少し解けてきましたが、まだわかっていないことが多いです。
クリップボードの保存先 や保存したリッチテキストの参照方法 も調べてみたいなと思いました。
ここまで読んでいただき、ありがとうございました。
2024/11/25 追記
見直していたら、「フォント」と「セルの折り返し」に少し違いがあることを発見しました。クリップボード奥が深いですね...
Register as a new user and use Qiita more conveniently
- You get articles that match your needs
- You can efficiently read back useful information
- You can use dark theme