JSFiddle - React, Tailwind, and code Playground

by sukobuto

HTML

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="https://raw.github.com/jquery/jquery-color/master/jquery.color.js"></script>
<h1>"Yellow Fade" を View 側で実装</h1>
<p>
    ViewModel は UI に対する依存をしないというのが、一応 MVVM のルールであるため、<br>
    foreach バインディングの afterRender 系レンダリングサポートを View 側で実装してみました。<br>
    PC版 &lt;-&gt; モバイル版 での View の載せ替えなどが予想される場合に有効です。<br><br>
    ViewModel のプロパティなのか View の関数なのかが紛らわしいという場合は、<br>
    View の関数を適当な名前空間(オブジェクト)でまとめるとよさそうです。
</p>
<hr>

<form data-bind="submit:addItem">
    追加するアイテム: <input type="text" data-bind='value:itemToAdd, valueUpdate: "afterkeydown"' />
    <button type="submit" data-bind="enable: itemToAdd().length > 0">追加</button>
</form>
 
<p>レシピ</p>
<ul data-bind="foreach: { data: allItems, afterAdd: yellowFade }">
    <li data-bind="text: $data,
                   css: { selected: $root.selectedItems.indexOf($data) > -1 },
                   click: $root.toggleSelect"></li>
</ul>
<script type="text/javascript">
    // View 側で Yellow Fade の実装を行う
    function yellowFade(element) {
        $(element).filter("li")
            .animate({ backgroundColor: 'yellow' }, 200)
            .animate({ backgroundColor: '#eeeeff' }, 800);
    }
</script>
 
<div>
    <button data-bind="click: removeSelected, enable: selectedItems().length > 0">削除</button>
    <button data-bind="click: sortItems, enable: allItems().length > 1">ソート</button>
</div>

CSS

h1 {
    font-size: 20px;
    margin: 10px 0;
}
ul {
    user-select: none;
}
li {
    background: #eeeeff;
    border: solid 1px #aaaaee;
    margin: 10px;
    cursor: pointer;
    width: 300px;
}
li.selected {
    background: #ffdddd;
}

JavaScript

$(function() {    

// ViewModel には UI に関する定義が一切無い状態    
var BetterListModel = function () {
    var self = this;

    this.itemToAdd = ko.observable("");
    this.allItems = ko.observableArray(["オリーブオイル", "パンチェッタ", "アボカド", "謎パスタ", "イタリアンパセリ", "岩塩ファサー", "追いオリーブ"]); // Initial items
    this.selectedItems = ko.observableArray(["Ham"]);                                // Initial selection
 
    this.addItem = function () {
        if ((this.itemToAdd() != "") && (this.allItems.indexOf(this.itemToAdd()) < 0)) // Prevent blanks and duplicates
            this.allItems.push(this.itemToAdd());
        this.itemToAdd(""); // Clear the text box
    };
 
    this.removeSelected = function () {
        this.allItems.removeAll(this.selectedItems());
        this.selectedItems([]); // Clear selection
    };
 
    this.sortItems = function() {
        this.allItems.sort();
    };

    this.toggleSelect = function(item, event) {
        if (event.ctrlKey) {
            if (self.selectedItems.indexOf(item) > -1) self.selectedItems.remove(item);
            else self.selectedItems.push(item);
        } else {
            self.selectedItems.removeAll();
            self.selectedItems.push(item);
        }
    };
};
 
ko.applyBindings(new BetterListModel());
});