获取祖先元素

时间:2022-12-20 20:35:49

在设计选择器时遇到的,当我们使用后代选择器,要从上一次的结果集中获取它们所有后代,可能存在重复元素。虽然排除重复元素是非常简单的事,但我们能不能从一开始就防范其生产重复元素呢?答案是肯定。

假设上一次的结果集有三个元素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)
        }