はじめてのwebサイト製作として、2つのtextareaの差分を色付けして表示する文章比較サイトを作りました。
目的
n予備校プログラミング入門コースを修了して何か作ってみようと思ったときに、n予備校時代にサンプルコードを写経して動かなかったときに使っていた文章比較サイトを改良してみようと思い作りました。
設計
「文章比較」とググってヒットするサイトは、見た限りでは2つのtextareaに文章を貼り付けたらボタンを押して下に色付きで差分が表示されるものでした。
そこでリアルタイム(ボタンなし)でtextarea内に直接差分を色付きで表示し、下には色付された行数と文字が表示されるものを作ろうと思いました。(textareaだけあれば行数表示はなくても十分使えるのでやめました)
まずは最初なのでhtml,css,javascriptだけで作ることにしました。
textareaに行数を表示する
こちらのライブラリーを使用することで左側に行数を表示できました。
文章を比較する
文章比較って、行ごと→単語ごとに左右でループさして比較するんじゃなくて、文字ごとにアルゴリズム(意味を十分には理解していない)で比較しないとうまくいかないことに今気づいた。どうしよう😭 pic.twitter.com/ijUT7hi13r
— 蟹江誠 (@KaneyHonest) March 10, 2021
当初は上のツイートのように考えていたのですがうまく比較できず、こちらもライブラリーを見つけて使用することにしました。
そのまま使うと改行の際に↵が出るのと追加した部分だけ、または削除した部分だけ色付けになるように改変しました
function diff_colorGreen(diffs) { var html = []; var pattern_amp = /&/g; var pattern_lt = /</g; var pattern_gt = />/g; var pattern_para = '↵'; for (var x = 0; x < diffs.length; x++) { var op = diffs[x][0]; // Operation (insert, delete, equal) var data = diffs[x][1]; // Text of change. var text = data.replace(pattern_amp, '&').replace(pattern_lt, '<') .replace(pattern_gt, '>').replace(pattern_para, '¶<br>'); switch (op) { case DIFF_INSERT: html[x] = ''; break; case DIFF_DELETE: html[x] = '<span style="background:#43FF6B;">' + text + '</span>'; break; case DIFF_EQUAL: html[x] = '<span>' + text + '</span>'; break; } } return html.join(''); }; function diff_colorRed(diffs) { var html = []; var pattern_amp = /&/g; var pattern_lt = /</g; var pattern_gt = />/g; var pattern_para = '↵'; for (var x = 0; x < diffs.length; x++) { var op = diffs[x][0]; // Operation (insert, delete, equal) var data = diffs[x][1]; // Text of change. var text = data.replace(pattern_amp, '&').replace(pattern_lt, '<') .replace(pattern_gt, '>').replace(pattern_para, '¶<br>'); switch (op) { case DIFF_INSERT: html[x] = ''; break; case DIFF_DELETE: html[x] = '<span style="background:#FF0033;">' + text + '</span>'; break; case DIFF_EQUAL: html[x] = '<span>' + text + '</span>'; break; } } return html.join(''); }; }
また、ライセンスがApache License, Version 2.0のためサイト内、githubにApache License, Version 2.0とライブラリーを使用している旨を表記しました。
textareaの裏に文章を表示させてその文章をtextarea内で操作している風にする
textareaと同じ階層のdivタグのさらに子要素をposition: absolute、z-index: -1にして力技でtextareaと重ね合わせて(textareaの裏にぴったりdivタグがある状態)、
以下のようにtextareaを透けさして裏を表示しました。
//textareaの背景と文字を透明化 $(".lined").css('background-color', 'transparent'); $(".lined").css('color', 'transparent'); $(".lined").css('caret-color', 'black');
textarea内の折返しについてはtextareaのwrap="hard"と裏のword-break: break-wordがシンクロしてくれました。
問題点
textareaとその裏のスクロールを連動させているのですが、改行だけだと裏のdivタグには一行追加されずスクロールがずれてしまうという問題点があります。。。
//textareaとスクロール対応 textareaRight.scroll(function() { var textareaScrollTopRight = textareaRight.scrollTop(); backScrollRight.scrollTop(textareaScrollTopRight); }); textareaLeft.scroll(function() { var textareaScrollTopLeft = textareaLeft.scrollTop(); backScrollLeft.scrollTop(textareaScrollTopLeft); }); //textareaにイベントが発生したなら textareaLeft.on('input', textMatch); textareaRight.on('input', textMatch); function textMatch() { //イベント時もスクロール反応 var textareaScrollTopRight = textareaRight.scrollTop(); backScrollRight.scrollTop(textareaScrollTopRight); var textareaScrollTopLeft = textareaLeft.scrollTop(); backScrollLeft.scrollTop(textareaScrollTopLeft); }
READMEを編集
GitHub Pagesでwebサイトを公開
やっとできたー!https://t.co/LwXkm8SL2b pic.twitter.com/M6A4Gd0T0S
— 蟹江誠 (@KaneyHonest) March 12, 2021
感想
最初はライブラリーというものがよくわかっていなかったのですが、2つも使えばなんとなく理解できました。
当初は文章比較部分のコードを100行以上javascriptで書いていたことによってjavascriptがちょっぴり理解できました。
教材でもライブラリーでもなく自分で書くことを通じてでしかプログラミングはうまくならないとひしひしと感じます。
GitHub
追記:コピーボタンを追加
差分を表示させたあとにそれを修正したものをコピーするボタンがあったら楽だと思い実装