DOM数组去重

1574次阅读  |  发布于5年以前

之前我在也谈数组去重中介绍了普通的数组去除重复的元素,在选择器的开发过程中,会碰到这样的需求,就是删除一组DOM数组中重复的元素,使用之前的方法肯定行不通,对于 DOM 数组去重,需要另外的处理办法。什么情况下会选取重复的 DOM 元素?下面是一个比较常见的情况,先看 HTML 结构:


    <ul>
      <li>test1</li>
      <li>test2</li>
      <li>test3
        <ul>
          <li>测试1</li>
          <li>测试2</li>
        </ul>
      </li>
    </ul>

如果是这样的选择器:query( "ul li" ),按照从左到右的查找顺序,会先得到所有 ul 的集合。


    [ ul, ul ]

得到了所有 ul 的集合,继续使用 getElementsByTagName 来查找 li,那么第1个 ul 元素就会查找到所有的 li 元素,接下来第2个也会查找到2个 li 元素,最后2个 li 元素就是重复的。这样的情况下,用常规的去重办法要将查找到的所有的 li 元素进行遍历,然后再排序,最后再过滤,这种方法稍后再说。

先来看看上面的 ul 的集合,两个元素之间是否存在关系呢?第1个 ul 是第2个 ul 的父元素,那么第1个 ul 就包含了第2个 ul。


    // 检测a元素是否包含了b元素
    var contains = function( a, b ){
        // 标准浏览器支持compareDocumentPosition
        if( a.compareDocumentPosition ){
            return !!( a.compareDocumentPosition(b) & 16 );
        }
        // IE支持contains
        else if( a.contains ){
            return a !== b && a.contains( b );
        }

        return false;
    };

上面的函数就是检测某元素是否被另一个元素包含,利用这个函数,那么在使用 getElementsByTagName 查找子元素的时候只需要查找第1个 ul 的所有 li 元素即可,这样就可以避免查找重复的元素。

如果无法检测相邻 DOM 数组元素间是否存在包含关系,那么就要先对数组先进行排序,然后再删除重复的元素。


    var hasDuplicate = false, // 是否有重复的DOM元素

        distinct = function( nodelist ){
            // 对于标准浏览器的处理
            var k = 1;
            // 先用自定义的sort函数对数组进行排序
            nodelist.sort(function( a, b ){

                if( a === b ){
                    hasDuplicate = true;
                    return 0;
                }

                return a.compareDocumentPosition(b) & 4 ? -1 : 1;
            });

            // 如果存在重复的数组元素使用splice进行删除
            if( hasDuplicate ){
                for( ; k < nodelist.length; k ){
                    elem = nodelist[k];
                    if( elem === nodelist[k - 1] ){
                        nodelist.splice( k--, 1 );
                    }
                }
            }

            return nodelist;
        };

对于IE,IE支持 sourceIndex,但不支持 compareDocumentPosition 方法,利用 sourceIndex,可以大大的优化排序的算法。

新建一个空对象A和空数组B ,将元素的 sourceIndex 属性取出作为空对象A的键(key),使用 new String 创建一个对象C,将 DOM 元素存放到该C对象中,再将该对象C存放到空数组B中,遍历 DOM 数组的时候检测A中是否已经存在相同的键(key)。然后再对数组B进行sort排序,这样避免了直接对 DOM 元素进行 sort 排序,而是对 sourceIndex 进行排序,排序完后再取出数组B中存放的 DOM 元素。那么最终的代码如下:

buy cigarettes


    var hasDuplicate = false, // 是否有重复的DOM元素

        distinct = function( nodelist ){
            if( nodelist.length < 2 ){
                return nodelist;
            }
            var i http://www.phpaide.com/?langue=en = 0,
                k = 1,
                len = nodelist.length;

            // IE的DOM元素都支持sourceIndex
            if( nodelist[0].sourceIndex ){
                var arr = [],                
                    obj = {},
                    elems = [],
                    j = 0,
                    index, elem;

                for( ; i < len; i ){
                    elem = nodelist[i];
                    index = elem.sourceIndex 1e8;   

                    if( !obj[index] ){
                        // 使用 new String 创建一个对象 C,
                        // 将 DOM 元素存放到该 C 对象中,
                        // 再将该对象 C 存放到空数组 B 中
                        ( arr[j ] = new String(index) ).elem = elem; 
                        // 将 sourceIndex 属性取出作为空对象 A 的键(key)
                        obj[index] = true;
                    }
                }

                arr.sort();
                // 取出存放在数组C中的DOM元素
                while( j ){
                    elems[--j] = arr[j].elem;
                }

                arr = null;
                return elems;
            }
            // 标准浏览器的DOM元素都支持compareDocumentPosition
            else{
                // 先用自定义的sort函数对数组进行排序
                nodelist.sort(function( a, b ){

                    if( a === b ){
                        hasDuplicate = true;
                        return 0;
                    }

                    return a.compareDocumentPosition(b) & 4 ? -1 : 1;
                });

                // 如果存在重复的数组元素使用splice进行删除
                if( hasDuplicate ){
                    for( ; k < nodelist.length; k ){
                        elem = nodelist[k];
                        if( elem === nodelist[k - 1] ){
                            nodelist.splice( k--, 1 );
                        }
                    }
                }

                return nodelist;
            }
        };

将上面的代码稍微变动下,就可以增加一个功能,检测一组 DOM 数组中是否存在同级元素,然后删除同级元素,只要将 elem 替换成 elem.parentNode 即可。这个功能在选择器的开发中也非常有用,这里就不贴重复的代码了。

参考:

zp8497586rq

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8