直近のこれまで 2 回に渡って、Swift から少し離れて余興的に Safari 拡張
を作ってみていますけれど、前回で完成までには至らないながらも要所は紹介できたことだし閉幕して Swift に戻っておこうかとも思ったのですけれど。
そう思いながら制作途中の Safari 拡張
を眺めてみると、やっぱり完成をみんなと分かち合えたら楽しいように思えたりしたので、せっかくなので今回も「Web ページ内の単語を ヌメロニム
化する Safari 拡張」の話題で進めていきますね。いよいよ表示中の Web ページに働きかけていけそうなのも楽しみにしつつ、どうぞよろしくお願いしますね。
—————————————————————————————————————— 熊谷さんのやさしい Swift 勉強会 #295
00:00 開始 00:10 Web ページのヌメロニム化、完成へ 01:48 ここまでの Safari Extension おさらい 03:59 実行時の検証環境準備おさらい 06:03 現時点では、テキストをピックアップしてヌメロニム化するところまで 07:28 対象外のテキストノードを除外する 09:01 DOM を学ぶのにオススメの本 12:21 除外したい項目をタグ名でフィルタリング 17:56 ひとまず、ヌメロニムに置き換えてみる 19:51 Swift.org をヌメロニム化してみよう 22:15 元のテキストがわかるように改良 30:06 単語単位で、元のテキストを調べられるように改良 34:36 Safari Extension 完成 ——————————————————————————————————————
Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #295
さて、始めていきますね。今日は引き続き、メロディムを使います。自分の中でなぜかこれが絶対にハマってしまっています。このインターナショナリゼーションを "I18N" で表現するというやつですね。これは、世間・世界を経験していたらどのようになるのかという興味から始めました。ウェブサイトの単語が全て ヌメロニム (数字表現)になったらどの世界になるのだろうというところから、Safari Web Extensionを作って実験してみようと考えました。これまでに3回取り組みました。今日はその3回目です。これをちょっと完成させたくて、前回で基本的なSafari Extensionの作り方については、一通り情報を話せた気がするため、あとは自分で作って結果を見せればいいかなとも思いました。しかし、アプリ作りなどでもそうですが、実際に動くところを体験するのが一番面白いですよね。そのため、それをやらないのはもったいないなと思ったのです。今日はそのあたり、実際にウェブページが動くところに行きたいと思います。
まずはおさらいです。前回来られていない方もいるかもしれないので、簡単にあらすじを話します。今回は、ウェブアプリケーションとしてSafari Web ExtensionをXcodeで作成しました。その中でどのウェブサイトに適用するか、たとえばTwitter、Wikipediaに対して適用するということです。ボタンが押されたらヌメロニムに変換しようという感じで、バックグラウンドの onClick
イベントをトリガーにして動作させる仕組みを作りました。メッセージを受け取ると、表示中のページのドキュメントのボディに対してヌメロニム変換を実行し、テキストノードであれば変換可能な部分をリプレイスし、置き換えていくという動作をします。今のところデバッグメッセージでコンソールログを出すところまでヌメロニム変換ができたという段階です。変換されたテキストをログに出力し、テキストノードでなければ子ノードに対して再帰的に実行して全てのノードを処理する仕組みになっています。
動かすときには、いくつかの下準備が必要です。デバッググラウンドプロセスをチェックする必要があります。SafariのWebエクステンションには、JavaScriptを使ってクライアント側で実行するものと、Swiftコードでネイティブ実行するものがあります。ネイティブコードの場合は、コンソールというアプリを使ってログをテストできます。そのため、一応ストリーミング開始として、ヌメロニムスというアプリを作成し、それにプロセスを仕込むと、ネイティブアプリのログがここに出てくるのでデバッグがしやすくなります(今回は使わないですが)。ブラウザー側では開発タブから未署名のエクステンションを有効化して、エクステンションを有効化します。既に有効化されているので、ボタンが押せるようになっています。これを押すとイベントが発火します。
実行するために用意しておきたいのがJavaScriptコンソールですね。ここにログが出てきます。いろいろとエラーが出ていますが、これは関係ないエラーです。また、バックグラウンドプロセスのログを見るためにバックグラウンドコンテンツをヌメロニムスエクステンションに接続しておきます。これで開発準備が基本的に整いました。このコンソールですね。
実行するときには、現時点ではログで ズバズバ
という感じでパスが表示されます。これはXcodeのログの部分です。パターンにマッチしたものが見つかったときには、クロージャが実行されてJavaScriptが動き、その中で中間の文字列をログとして出します。このパターンは単語境界があり、最初の文字のサブパターン、中間の文字列、最後の文字のサブパターンから構成されているということです。 なので、Mittles
は単語の境界が取れるという状況になっています。例えば、Safariでは単語の境界がどのように見つかるのかよくわかりませんが、単語の境界が正確に出てきます。そして、この単語の境界を利用して文字列を置き換えるのですが、その前に少し気になることがあります。
これはJavaScriptに関する問題ですが、怪しいコードが含まれている場合、ウェブサイト全体の動作に影響しそうです。このため、まずは余計なものを除外する必要があります。
通常の文字列やテキストっぽいものは直したいのですが、余計なものを除外するために、まずは子要素(Child)を検査します。このとき、const tagName
を使い、Child.tagName
を確認します。この操作にはDOM(Document Object Model)を使用します。
例えば、element.tagName
でタグ名を取得できます。これはウェブを操作する際に非常に便利で面白い技術です。自分がこの操作を勉強したのは、AJAXに関する本を参考にしました。Google Mapsが登場した際に注目されたAJAXという技術を使って、JavaScriptでDynamic HTMLを操作する方法を学びました。
この本は非常に読みやすく、DOMの使い方について詳しく説明されています。JavaScriptが進化し、便利になったことで、少々古い内容かもしれませんが、基本的な部分は変わらないので、十分参考になると思います。
では、タグ名を取得し、コンソールに出力してみます。コードでは以下のようにします。
const tagName = child.tagName;
console.log(tagName);
これを実行すると、タグ名がコンソールに出力されます。ただし、もしスクリプトタグなどの不要なタグが含まれていたら、これを除外しなければなりません。
例えば、以下のようにすることでスクリプトタグを除外できます。
if (tagName === 'SCRIPT') {
continue;
}
これでスクリプトタグは除外されるので、余計なエントリが少なくなります。さらに、ノースクリプトやスタイルタグも除外リストに加えましょう。配列を使って除外するタグリストを管理するのが良いです。
const excludeTags = ['SCRIPT', 'NOSCRIPT', 'STYLE'];
if (excludeTags.includes(tagName)) {
continue;
}
このような形で、不要なタグを除外しながら作業を進めていくと、効率よくコードを整理できます。 とりあえず、ここで一段落しましょう。タグネームについてですが、これはインクルードとかコンティニューのように使うものですね。タグネームを見せる必要はあまりないかもしれませんが、ログを消してから進めると良いかもしれません。
ログを消して、再度実行するとエラーが出ずに動くかもしれません。インクルードに関してですが、その違いが少し気になりますね。ここでログをたくさん出しておきましょう。インクルードとタグネームの違いが原因かもしれません。
また、JavaScriptのインクルード関数についても調べてみると良いでしょう。雰囲気で使っているかもしれませんが、スタンドガイドラインに従ってやるのが良いでしょう。適当に使うのではなくて、しっかりと調査した上で使うようにすると良さそうです。これで大体の準備は整いました。
具体的な置き換え方法についてですが、テキストノードのコンテンツを入れ替える方法を試してみましょう。例えば、node.textContent = newText
のようにテキストを入れ替えるやり方です。実行してみれば変わるのが確認できると思います。
では、実際に置き換えを行うテキストをノードのテキストコンテンツに代入してみましょう。結果がどうなるか試してみると、画面にそのまま反映されるはずです。実装したコードを実行してみると、一瞬ですがテキストが変わるのが分かりました。
その後、他のページでも試してみましょう。例えば、英語の多いページで試してみると変化が分かりやすいかもしれません。URLを追加してビルドし、テキストが変わるか確認します。このように進めると良さそうですね。
基本的にはテキストの置き換えはうまくいっているようです。ただ、日本語のページではインパクトが薄いかもしれません。他の言語やページで試してみるのも良いかもしれません。
ここまでの内容で大体の準備は整ったと思います。何か予想外の動きがあった場合には、そのときに修正を加えれば良いでしょう。 それでは実際にセットアップを進めていきましょう。まず、テキストを置き換えるのではなくて、HTMLのボルト設定を試してみます。
要素検証ツールを使って、例えば<h1>
タグに対してボルト設定を適用すると、ポップアップが表示されるか試してみたいですね。以下のように、<span>
タグを使ってセットアップします。例えば、JavaScriptで次のようにコードを書きます。
const node = document.createElement('span');
node.setAttribute('title', 'ここにタイトルテキストを入れます');
node.innerText = 'ヌメロニムのテキスト';
次に、以下の手順で進めます。
const textNode = document.createTextNode('ヌメロニムのテキスト');
node.appendChild(textNode);
parentNode.replaceChild(node, oldNode);
createTextNode
を使ってテキストノードを作成し、そのテキストをノードに追加します。そして、既存のノードを新しいノードに置き換えます。
例えば、次のようにreplaceChild
メソッドを使います。
const newNode = document.createElement('span');
newNode.setAttribute('title', 'このタイトルは多くの要素で使えます');
const textNode = document.createTextNode('ヌメロニムのテキスト');
newNode.appendChild(textNode);
const parentNode = oldNode.parentNode;
parentNode.replaceChild(newNode, oldNode);
これで、新しいノードが正しく追加されたことを確認できます。parentNode.replaceChild
を使用して、新しいノードnewNode
を旧ノードoldNode
と置き換えます。
最後に、ビルドして実行します。この操作で、スクリーン上のテキストがどのように変わるかを確認します。しかし、もしテキストが期待通りに表示されない場合には、デバッグツールを使って問題の箇所を確認すると良いでしょう。例えば、以下のようにコンソールログを追加するとデバッグが簡単になります。
console.log(newNode);
console.log(parentNode);
これにより、ノードの状態や親ノードの状態を確かめることが可能です。ビルド後に表示が正しくなったことを確認できたら、無事に完了です。
このように進めれば、HTML内のテキストを動的に置き換えることができます。問題が発生した場合には、JavaScriptコンソールを確認して、修正が必要な箇所を特定することが重要です。