@ledsun blog

Hのキーがhellで、Sのキーがslaveだ、と彼は思った。そしてYのキーがyouだ。

キャッシュカード落とした

銀行のキャッシュカードをATMに置き忘れました*1

朝、ATMに行って入金し、夜に財布の中にキャッシュカードがないことに気がつきました。 ATMの置き忘れの確率が高いのですが、気づいた時間が遅すぎるため、日中の移動中にどこかで落とした可能性もあります。

次の日、ATMから一番近い公共施設、駅の改札に行って聞いてみました。 キャッシュカードの落としものはあるものの名前が一致しないそうです。 また、この駅では改札外での落とし物は、駅の外なので、交番に誘導するそうです。

交番で確認したらありました。 管轄の警察署にありました。 前日の朝、届けられ、聞きに行った日にはすでに警察署に移動されていたようです。 警察の落とし物照会は電話でした。 2018年、平成も終わろうという年に電話です。 電車の落とし物紹介は電子化されていました。 なかなか驚きです*2。 結局、ATMに置き忘れで届けてくれた人がいたそうです。 届けられた時刻は、落としてから、30分も経っていませんでした。 親切な人はいるものです。

一応、銀行に電話してキャッシュカードを止めていました。 その時、不正利用がないことも確認してくれました*3。 生体認証を登録していたので、不正利用される確率はほとんどありません。 生体認証は、銀行に連絡するまでの間も安心感があっていいですね。

これから、銀行窓口でキャッシュカードを再開してきます*4

*1:今まで、忘れたことがないので驚きました。加齢による衰えでしょうか?

*2:災害に強いインフラを使うという考慮があるのでしょうか?それとも単に予算不足なのでしょうか?

*3:口座情報はマネーフォワードに登録していたので、スマートフォンアプリで確認済みでした

*4:お届け印と本人確認書類が必要だそうです

DOM更新アルゴリズムを実装しました

github.com

動機

virtual-domの良さ

Reactに代表されるようにGitHub - Matt-Esch/virtual-dom: A Virtual DOM and diffing algorithmを使うと、デザイン変更時に、JavaScriptのロジックを考えずに、HTMLとCSSを考えるだけよくなることがわかっています。

一方でvirtual-domはGitHub - hyperhype/hyperscript: Create HyperText with JavaScript.形式のAST(抽象構文木)を自分で作る必要があります。そこでReactではIntroducing JSX - Reactという新しい記法を作って、HTMLライクに記述できる様にしました。

JSXの悪さ

JSXには2つ問題があります。

  1. HTMLではない
  2. Babel · The compiler for writing next generation JavaScriptでJSXからJavaScriptに変換する必要がある

最初から、Reactを使うのであれば問題ありません。 既存のWebアプリケーションに部分的に適用する場合は、BabelとJSXの投入は大きな変更です。 JSXは、HTMLのようでHTMLではありません。HTMLからJSXに置き換える作業が必要です。 ワークフロー上、Babelを使っていなかった場合は、新規にBabelを使用する必要があります。

HTMLを入力とする

そこで、HTMLを入力としてDOMを更新する関数を作成しました。 Handlebars.js: Minimal Templating on Steroidsの様なHTMLを生成するテンプレートエンジンを使っている場合は、生成したHTMLをそのまま入力値として使えます。 テンプレートエンジンを使っていない場合でも、現在表示しているDOMを、Google Chromeの開発ツールを使ってHTML形式でコピーすることで、流用できます。 この場合は、動的に置き換えたい値をテンプレートリテラルやテンプレートエンジンを使って置き換える作業が必要です。

設計

大方針

  1. GitHub - inikulin/parse5: HTML parsing/serialization toolset for Node.js. WHATWG HTML Living Standard (aka HTML5)-compliant.を使ってHTMLをASTに変換
  2. ASTを辿ってDOMに反映

細かい知見

appendChildはツリーの帰り道で行う

DOMにNodeを追加すると、その度にNode追加後の表示要素の場所を再計算します。 Nodeを追加する際は、Nodeとその子Nodeを作成してから、Node.appendChild - Web API インターフェイス | MDNします。

ツリーを辿る順序では行きでNodeを作成し、帰りでNodeを追加します。 ロジックとしては、子Nodeの更新が終わった後に、appendChildします。

Nodeの型の変更

DOMは既存のNodeの型を別の型に変更できません。 例えばdivspanに変更できません。またElement(HTMLタグ)をTextNode(タグの中身)に変更すること、その逆もできません。

型が変わった時は、新しいNodeを作成し、Node.replaceChild - Web API インターフェイス | MDNを使って既存のNodeと入れ替えます。

boolean attributes

Nodeにはboolean attributesという属性があります。

例えば

  • disabled="disabled"
  • checked="checked"

です。 これらはDOM上の属性をelement.setAttribute - Web API インターフェイス | MDNで変更しても、ブラウザ上の見た目には反映されません。

Node.disabledなどの、個別のプロパティを併用する必要があります。

やっていないこと

リストへの挿入時の最適化はしていません。 liタグなどで作ったリストに、最後尾への追加でなく、途中への挿入をした場合、それ以降の要素は全て変更したとして扱います。

経緯

Ruby でつくるRubyを読んでASTの辿り方を学んだ

ledsun.hatenablog.com

わずか120行のソースコードを写経するだけで、ASTの扱い方が理解できる本です。

update-dom-tree基本戦略は、この本のASTの扱いと一緒です。 ASTを辿ると同時に評価し、DOMに反映します。

virtual-domは、差分検知と更新の二段階に分かれています。 変更を検知したNode以下を作り直し、replaceChildします。

Node.jsのスクレイピング情報を整理してHTMLのASTを得る方法を学んだ

qiita.com

スクレイピングのは

  1. HTMLの取得
  2. HTMLの評価

の2要素からなります。 このうちHTMLの評価ではHTMLのパースが必要です。 色々な方法があります。この時の知見からparse5を選択しました。

  1. pure JavaScript
  2. メジャーなライブラリで使われている
  3. 更新が活発

の3点を重視しました。

結果

デザイン変更の自由度の上がりっぷりは最高でした。

  1. HTMLでイメージに合ったUIを作る
  2. handlebarsを使ってデータとテンプレートに分ける
  3. update-dom-treeを使ってビュークラスを作る
  4. モデルクラスを作って、データ更新のAPIとイベントを作る

という手順でコンポーネントが作れます。 UIの

  • 表示項目
  • 表示イメージ
  • データの取得方法

が固まっている時に、サクサク作れて便利です。

実装にかかった時間は、初期実装とデバッグを合わせても丸1日程度でした。 それに比較すると、デザイン変更の自由度の上昇はかなり大きな利得です。

記録