Knockoutjs实战开发:自定义标签支持虚拟元素(Creating custom bindings that support virtual elements)
Knockoutjs的控制流程绑定(比如:if和foreach)不仅仅可以绑定在一个真实的DOM元素上,我们也可以将其绑定到一个虚拟的DOM元素上,这个DOM是由一个特殊语法定义的。比如:
1 <ul> 2 <li class="heading">My heading</li> 3 <!-- ko foreach: items --> 4 <li data-bind="text: $data"></li> 5 <!-- /ko --> 6 </ul>
我们自定义的标签也能向上例一样支持虚拟元素,但是,为了使用此功能,我们必须明确的告诉Knockoutjs我们自定义的绑定是支持虚拟元素的,此时我们可以使用ko.virtualElements.allowedBindings
方法来通知Knockoutjs。
例1、作为开始,我们首先自定义一个绑定,如下:
1 <script type="text/javascript"> 2 ko.bindingHandlers.randomOrder = { 3 init: function (elem, valueAccessor) { 4 // Pull out each of the child elements into an array 5 var childElems = []; 6 while (elem.firstChild) 7 childElems.push(elem.removeChild(elem.firstChild)); 8 9 // Put them back in a random order 10 while (childElems.length) { 11 var randomIndex = Math.floor(Math.random() * childElems.length), 12 chosenChild = childElems.splice(randomIndex, 1); 13 elem.appendChild(chosenChild[0]); 14 } 15 } 16 }; 17 </script>
此时我们自定义的绑定在真实的DOM元素中是可以起作用的,下面的元素会按照随机数重新进行排序:
<div data-bind="randomOrder: true"> <div>First</div> <div>Second</div> <div>Third</div> </div>
而我们自定义的绑定则不会在虚拟DOM元素中起作用。
<!-- ko randomOrder: true --> <div>First</div> <div>Second</div> <div>Third</div> <!-- /ko -->
你可能还会获得以下的错误:The binding 'randomOrder' cannot be used with virtual elements
。为了让我们自定义的绑定在虚拟DOM元素上起作用,我们可以使用下面的语句来通知Knockoutjs。
ko.virtualElements.allowedBindings.randomOrder = true;
此时,可能不会报错了,但是我们自定义绑定依然没有起作用,这是因为我们使用的编码方式并不能支持虚拟元素,这也就是为什么Knockoutjs会让我们选择性的支持虚拟元素:除非你编写的自定义绑定代码也支持虚拟元素,否则即使你加入上面的话,也不会起作用的。
因此我们将上例中的自定义绑定做如下的修改:
<script type="text/javascript"> ko.bindingHandlers.randomOrder = { init: function (elem, valueAccessor) { // Build an array of child elements alert("bb"); var child = ko.virtualElements.firstChild(elem), childElems = []; while (child) { childElems.push(child); child = ko.virtualElements.nextSibling(child); } // Remove them all, then put them back in a random order ko.virtualElements.emptyNode(elem); while (childElems.length) { var randomIndex = Math.floor(Math.random() * childElems.length), chosenChild = childElems.splice(randomIndex, 1); ko.virtualElements.prepend(elem, chosenChild[0]); } } }; ko.virtualElements.allowedBindings.randomOrder = true; </script>
此时,我们的虚拟标签就可以使用了。此时我们使用ko.virtualElements.firstChild(domOrVirtualElement)代替了
domElement.firstChild
,
此例的完整代码如下:
1 <script type="text/javascript" src="knockout-2.2.0.js"></script> 2 3 4 ------------------------------------- 5 <!-- ko randomOrder: true --> 6 <div>First</div> 7 <div>Second</div> 8 <div>Third</div> 9 <!-- /ko --> 10 <script type="text/javascript"> 11 ko.bindingHandlers.randomOrder = { 12 init: function (elem, valueAccessor) { 13 // Build an array of child elements 14 alert("bb"); 15 var child = ko.virtualElements.firstChild(elem), 16 childElems = []; 17 while (child) { 18 childElems.push(child); 19 child = ko.virtualElements.nextSibling(child); 20 } 21 22 // Remove them all, then put them back in a random order 23 ko.virtualElements.emptyNode(elem); 24 while (childElems.length) { 25 var randomIndex = Math.floor(Math.random() * childElems.length), 26 chosenChild = childElems.splice(randomIndex, 1); 27 ko.virtualElements.prepend(elem, chosenChild[0]); 28 } 29 } 30 }; 31 ko.virtualElements.allowedBindings.randomOrder = true; 32 </script> 33 34 <script type="text/javascript"> 35 var viewModel = { 36 giftWrap: ko.observable(true) 37 }; 38 ko.applyBindings(viewModel); 39 </script> 40 41 42 43
虚拟元素的API大家可以点击:http://knockoutjs.com/documentation/custom-bindings-for-virtual-elements.html查看,这里就不介绍了。