動機
リストの絞り込みのサンプルはあったし、テキストをハイライトするサンプルも見つかった。
しかし両方をこなしているサンプルが見つからなかったから作ってみた。
前提
- データ取得は別なのでデータとなる配列はべた書き
- 絞り込み時には大文字小文字を区別しない
コード
HTML
<div class="content">
<label>検索:
<input type="text" class="input_highlight" data-bind="value: searchBrand, valueUpdate: 'afterkeydown'">
</label>
<ul data-bind="highlightForeach: {data: filteredBrands, search: searchBrand}">
<li>
<a data-bind="attr: {href: url}">
<span data-bind="html: name"></span>
<span data-bind="html: alias"></span>
</a>
</li>
</ul>
</div>
JavaScript
ko.bindingHandlers.highlightForeach = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
ko.bindingHandlers.foreach.init(element, function () { return []; }, allBindings, viewModel, bindingContext);
},
update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
var options = valueAccessor();
var search = ko.utils.unwrapObservable(options.search);
var highlight = ko.utils.unwrapObservable(options.data).map(function(value, index) {
var name, alias;
if (search.length > 0) {
name = value['name'].replace(new RegExp(search, 'gi'), '<span class="yellow">' + '$&' + '</span>');
alias = value['alias'].replace(new RegExp(search, 'gi'), '<span class="yellow">' + '$&' + '</span>');
} else {
name = value['name'];
alias = value['alias'];
}
return { url: value['url'], name: name, alias: alias};
});
ko.bindingHandlers.foreach.update(element, function() { return highlight; }, allBindings, viewModel, bindingContext);
}
};
var vm = function() {
var self = this;
self.searchBrand = ko.observable('');
self.brands = ko.observableArray([
{url: "http://www.sony.jp/", name: "ソニーモバイルコミュニケーションズ", alias: "SONY MC"},
{url: "http://www.samsung.com/jp/", name: "サムスン電子", alias: "Samsung"},
{url: "http://www.htc.com/jp/", name: "エイチティーシー", alias: "HTC"},
{url: "http://www.lg.com/jp", name: "LGエレクトロニクス", alias: "LG Electronics"},
{url: "http://www.asus.com/jp/", name: "アスース", alias: "ASUSTek"},
{url: "http://www.huawei.com/jp/", name: "ファーウェイ", alias: "Huawei"},
{url: "http://www.motorola.co.jp/", name: "モトローラ・モビリティ", alias: "Motorola Mobility"},
{url: "http://www.sharp.co.jp/", name: "シャープ", alias: "SHARP"},
{url: "http://jp.fujitsu.com/", name: "富士通", alias: "FUJITSU"},
{url: "http://www.kyocera.co.jp/", name: "京セラ", alias: "KYOCERA"},
{url: "https://www.apple.com/jp/", name: "アップル", alias: "Apple"},
{url: "http://www.nokia.com/global/", name: "ノキア", alias: "Nokia"}
]);
self.filteredBrands = ko.computed(function() {
var filter = self.searchBrand().toLowerCase();
if (!filter) {
return self.brands();
} else {
return ko.utils.arrayFilter(self.brands(), function(brand) {
if (brand['name'].toLowerCase().indexOf(filter) !== -1) {
return true;
} else if (brand['alias'].toLowerCase().indexOf(filter) !== -1) {
return true;
}
return false;
});
}
});
};
ko.applyBindings(new vm());
簡単な解説
HTMLもJavaScriptも大きく2つに分けられる。
filteredBrands
の部分とhightlightForeach
の部分である。
self.filteredBrands
ではdata-bind="value: searchBrand”
としているsearchBrandの値を見て、空であれば元々の値を、そうでなければko.utils.arrayFilter
を使ってフィルタリングを行っている。
ko.utils.arrayFilter
でtrue
が返った要素のみko.computed
によって表示が行われる。
ko.bindingHandlers.highlightForeach
ではKnockoutJSにあるforeachを拡張する形で新たなハンドラを定義している。
update
でsearchBrand
が更新された際にupdate
が呼ばれ、highlight
という値でfoeachでループを回す前に値
を正規表現で置き換えしている。
動作例
JSFiddleを使って動かせるようにしてみた。
http://jsfiddle.net/nana4gonta/t85ywshv/