DOM操作をノックアウト！Knockoutで実現する変更に強いUI

DOM操作大変ですよね。ノックアウトしたいって思いますよね。
そこで今回ご紹介するのは、よりシンプルに変更に強いコードにしてくれる……かもしれないJavaScriptライブラリ「Knockout」です。

Knockoutとは
------------
[Knockout.js 日本語ドキュメント](http://kojs.sukobuto.com/docs/introduction)によると

> Knockout は リッチでレスポンシブなUIデザインの開発を助け、比較的大規模な開発であっても簡潔なデータモデルを保つことができる JavaScript ライブラリです。 ユーザの操作による状況の変化や、外部データソースの変更などにより動的に変更されるUIを作る際に、Knockout はよりシンプルかつ保守しやすいように実装する手助けとなります。

とのこと。ここでKnockoutの使い方について簡単にご説明しましょう。

Knockoutの使い方
---

KnockoutはModel-View-ViewModel (MVVM) モデルを簡単に実現することができるライブラリです。
ここで特に知っておきたいのは「View」と「ViewModel」です。
ViewModelはHTMLとJavaScriptの双方から参照・変更することのできるデータ、ViewはViewModelを元にした画面への表示を意味します。
JavaScriptでViewModelを更新すれば、Viewすなわち画面の表示も更新され、画面の表示であるViewを変更すれば、ViewModelも更新することができるのです。

その実現のために使われているのは独自の属性である「data-bind」と、ViewModelの変更を検知し自動的に画面に反映する仕組み「Observable」です。

### data-bind

data-bind属性はhtmlのタグの中に記載することで、画面とViewModelを紐付ける役割をします。
「data-bind="text: xxxx"」のように「data-bind」と「バインディング名:値」をイコールで繋ぐ形で表現します。このように「text:」であればタグ内の値に対してデータを反映させますが、この「text」の部分を「css」や「visible」などに書き変えることによって、データを用途に合わせて紐付けることができます。

### Observable

先程も簡単にご紹介したとおり、Knockokutの特徴である、ViewModelの変更を検知し自動的に画面に反映するという仕組みを実現するのがこのObservableです。ViewModelのプロパティをObservableとして定義することで、ViewModelの変更を自動的に検知し画面に反映させることができます。

なんだか「よりシンプルで変更に強いコード」にしてくれそうな気配がしますね。
百聞は一見にしかずとも申しますし、実際にサンプルコードを使ってどのような変化があるのか見ていきましょう。

まずはjQueryでサンプルコードを書き、次にそれと同じ機能をKnockoutを使って実装していきます。

まずはjQueryで書いてみよう
---

２重の入れ子になったリストを用意し、その一部分をフォームに入力した値に書き換えます。
ここでは、「フォームの入力でここが変化する」の値を書き換えるようにしました。

```html:sample.html
<html>
<head>
    <meta charset="utf-8" />
    <script src="jquery-3.4.1.min.js"></script>
    <script src="sample.js"></script>
</head>
<body>
    入力：<input>
    <ul>
        <li>変化しない</li>
        <li>変化しない
            <ul>
                <li>変化しない</li>
                <li>フォームの入力でここが変化する</li>
            </ul>
        </li>
    </ul>
</body>
</html>
```
```Javascript:sample.js
$(function(){
    $('input').change(function(){
        var text = $(this).val();
        $('ul:nth-child(1)').children('li:nth-child(2)').text(text);
    });
});
```
jQueruyでは値の取得、書き換えはJavaScriptでDOMを指定して行います。
フォームの<input>は1つしかないので簡単に値を取得することができました。
しかし、リストは２重の入れ子構造になっているため、何番目のどの値を書き換えるのか指定しなければなりません。
今回はリストの数が少なかっため簡単に場所を特定することができましたが、より大きなリストになると数えるのが少し大変かもしれませんね。

Knockoutで書き換えてみよう
---

それでは実際にKnockoutで書き換えたコードを見てみましょう。

```html
<html>
<head>
    <meta charset="utf-8" />
    <script src="knockout-3.5.1.js"></script>
    <script src="sample-ko.js"></script>
</head>
<body>
    入力：<input data-bind="value: text">
    <ul>
        <li>変化しない</li>
        <li>変化しない
            <ul>
                <li>変化しない</li>
                <li data-bind="text: text">フォームの入力でここが変化する</li>
            </ul>
        </li>
    </ul>
</body>
</html>
```
```Javascript
window.onload = function() {
    var ViewModel = function() {
        this.text = ko.observable();
    }
    ko.applyBindings(new ViewModel());
};
```

フォームの値はinputタグの「data-bind="value: text"」によってViewModelの「text」に紐付けられており、observableが変更を検知すると自動的に「text」の値を変更します。そのため、JavaScriptで値を取得するためのコードを書く必要はありません。HTMLへの値の設定も同じくobservableが画面に反映させるので自分で値を設定する必要はありません。
また、どのタグに値を反映させる場所かはhtmlに「data-bind="text: text」を記述することで紐付けており、JavaScriptからDOMを指定する必要はありません。

JavaScriptでDOM操作なしで、同じ機能を実装することができました。
DOMの操作をしていないということは、JavaScriptがhtmlの構造を把握する必要がなくなる、つまりhtmlとJavaScriptが疎結合になったということを意味します。

また、データの紐付け場所はhtml内にdata-bind属性で記述するため、どの値がどのタグに表示されるのかとても分かりやすくなりました。

Knockoutを使ったことで、見た目にはとても良さそうな感じになってきました。が、本当にUIの変更に強いコードになっているのでしょうか。
今度はUIを少し変更し、UIの変更でJavaScriptがどのような影響を受けるのか見ていきましょう。

変更してみよう
---

先程のサンプルコードのリストに一行追加してみます。

まずはjQueryを使って実装したコードです。

```html:sample.html
// ... 略
<body>
    入力：<input>
    <ul>
        <li>変化しない</li>
        <li>変化しない
            <ul>
                <li>変化しない</li>
                <li>新しく行を追加</li>
                <li>フォームの入力でここが変化する</li>
            </ul>
        </li>
    </ul>
</body>
// ... 略
```
```Javascript:sample.js
$(function(){
    $('input').change(function(){
        var text = $(this).val();
        // HTMLの変更に伴い、取得するDOMの変更が必要
        $('ul:nth-child(1)').children('li:nth-child(3)').text(text);
    });
});
```
お次はKnockoutを使って書いたコードです。

```html:sampke-ko.html
// ... 略
<body>
    入力：<input data-bind="value: text">
    <ul>
        <li>変化しない</li>
        <li>変化しない
            <ul>
                <li>変化しない</li>
                <li>新しく行を追加</li>
                <li data-bind="text: text">フォームの入力でここが変化する</li>
            </ul>
        </li>
    </ul>
</body>
// ... 略
```
```Javascript:sample-ko.js
window.onload = function() {
    var ViewModel = function() {
        this.text = ko.observable();
    }
    ko.applyBindings(new ViewModel());
};
```
jQueryでは表示の位置の変更に伴いJavaScriptを変更したのに対し、Knockoutはhtmlの変更のみで実装を完了することができました。
いかがでしょう！これはUIの変更に強いコードになったと言えるのではないでしょうか。

まとめ
---
Knockoutは、data-bind属性を使うことでHTMLとJavaScriotを分け、タグ取得のためのidやclassの設定を不要にし、JavaScriptがUIの変更を意識しなくてもよくなるとても便利なライブラリです。

とはいえ、今回のように簡単なプログラムを見ていると「タグにidを付与して、JavaScriptでidを取得すれば、UIを変更しても影響は少ないのでは？？」と思ってしまいます。
もっと複雑な処理が必要な場合、例えばデザイナーさんがHTML、エンジニアさんがJavaScriptというように担当が分かれる開発でこそ、UIと処理を分離できるKnockoutが真価を発揮するのかもしれませんね。

さてさて2019年もあとわずか。2020年はKnockoutでDOM操作をノックアウトしませんか？

参考
---
[Knockout.js 日本語ドキュメント](http://kojs.sukobuto.com/docs/introduction)