在设计选择器时遇到的,当我们使用后代选择器,要从上一次的结果集中获取它们所有后代,可能存在重复元素。虽然排除重复元素是非常简单的事,但我们能不能从一开始就防范其生产重复元素呢?答案是肯定。
假设上一次的结果集有三个元素a,b,c,如果其中两个存在包含关系,即是说A的parentNode或祖先是B,那么一定会出现重复的子集。既然如此,一开始,我们把A去掉,就没问题了。
看下面网页:
<!doctype html> <html dir="ltr" lang="zh-CN"> <head> <meta charset="utf-8"/> <title>排除 重复元素 by 司徒正美</title> </head> <body id="id10"> <p id="id1">一<span id="id2">二</span><span id="id3">二</span><span class="bbb">二</span></p> <p id="id4">一<span id="id5">二</span></p> <p title="aaa">一<span title="aaa">二</span></p> <div id="id6"> <p id="id7"><strong id="id8">Strong<span id="id9">二</span></strong></p> </div> </body> </html>
我们从它里面选择几个元素组成数组,模拟为选择器上一次选取的结果。
window.onload = function(){ var $ = function(id){ return document.getElementById(id); } var a = $("id1"); var b = $("id2") var c = $("id3") var d = $("id4") var e = $("id5") var f = $("id6") var g = $("id7") var h = $("id8") var arr = [a,b,c,d,e,f,g,h]; }
由于网页很简单,我们一眼就看得出,最终应该从中筛选出:a,d,f。
要实现这个目标,我们必须先对数组进行排序,让元素安排它们在页面上的位置从上到下,从左到右排序。这简单, 我们可以通过以下方法实现:
var sortOrder = function(){ if(!+"\v1"){ return function( a, b ) { return a.sourceIndex - b.sourceIndex; } }else{ return function(a,b){ a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1; } } }();
我们还需要检测它们是否存在包含关系:
var contains = function(a,b){ if(!+"\v1"){ return a === b ? false : a.contains(b); }else{ return !!(a.compareDocumentPosition(b) & 16) } }
最后我们进行两两比较,把儿子与孙子都找出来,打上标记,没有标记的就是我们要找的祖先,那么从这些祖先获取的后代集合就一定没有重复元素了!
arr.sort(sortOrder); var getAncestor = function(array){ for (var i = 0,n=array.length; i < n; i++) { for (var j = n - 1; j > i; j--) { var a = array[i],b=array[j]; if (contains(a,b)) { b.no = 1; }else if(contains(b,a)) { a.no = 1 } } } var result = [],ri= 0 for(var k=0;k<n;k++){ if(!array[k].no){ result[ri++] = array[k] } } return result; } var ancestor= getAncestor(arr)
但是这样做有个致命的缺陷,就是只能筛选一次,下一次这个no私有属性将成为干扰因素,但移除私有属性费时费劲,因此建议使用uuid技术进行筛选。
arr.sort(sortOrder); var uuid = 0 var getAncestor = function(array){ var remove = {},uid ; for (var i = 0,n=array.length; i < n; i++) { for (var j = n - 1; j > i; j--) { //比如一共有五个元素,那么每趟为 var a = array[i],b=array[j]; if (contains(a,b)) { uid = b.uuid || (b.uuid = "dom-"+uuid++) ; remove[uid] = 1; }else if(contains(b,a)) { uid = a.uuid || (a.uuid = "dom-"+uuid++); remove[uid] = 1; } } } var result = [],ri= 0 for(var k=0;k<n;k++){ if(!remove[array[k].uuid]){ result[ri++] = array[k] } } return result; } var ancestor = getAncestor(arr) for(var i=0,n=ancestor.length;i<n;i++){ alert(ancestor[i].id) }