根据DOM在DOM中的位置对包含DOM元素的数组进行排序

时间:2014-03-24 14:52:48

标签: javascript jquery html arrays dom

上下文

我已经构建了一个jQuery插件,我正在以一种方式将DOM元素存储在一个数组中,主要是为了能够在这些元素旁边存储更多信息,而不必使用不那么多的东西。快data()

该数组看起来像:

[
    { element: DOMElement3, additionalData1: …, additionalData2: … },
    { element: DOMElement1, additionalData1: …, additionalData2: … },
    { element: DOMElement2, additionalData1: …, additionalData2: … },
]

此插件的工作方式阻止我以可预测的顺序将这些元素推送到数组,这意味着DOMElement3实际上可以在低于DOMElement2的索引处找到自己。

但是,我需要按照它们包含的DOM元素在DOM中出现的顺序排列这些数组元素。前面的示例数组一旦排序,将如下所示:

[
    { element: DOMElement1, additionalData1: …, additionalData2: … },
    { element: DOMElement2, additionalData1: …, additionalData2: … },
    { element: DOMElement3, additionalData1: …, additionalData2: … },
]

当然,如果DOMElement1出现在DOM中的DOMElement2之前,DOMElement2出现在DOMElement3之前。

废弃解决方案

jQuery add()方法以与DOM中出现的顺序相同的顺序返回一组DOM元素。我可以使用它,但要求是我使用jQuery集合 - 这意味着我必须重构上述插件的大部分以使用不同的存储格式。这就是为什么我认为这是最后的解决方案。

另一种解决方案?

我原本想象map()和一种与每个DOM元素绑定的全局DOM索引都可以完成这个技巧,但似乎没有这样的“全局DOM索引”。

您能想到什么方法? (如果编写代码,欢迎使用vanilla JS和jQuery。)

4 个答案:

答案 0 :(得分:13)

有一个非常有用的函数叫compareDocumentPosition,它根据两个元素相对于彼此的位置返回一个数字。您可以在.sort回调中使用此功能:

yourArray.sort(function(a,b) {
    if( a === b) return 0;
    if( !a.compareDocumentPosition) {
        // support for IE8 and below
        return a.sourceIndex - b.sourceIndex;
    }
    if( a.compareDocumentPosition(b) & 2) {
        // b comes before a
        return 1;
    }
    return -1;
});

答案 1 :(得分:1)

您不必进行大量重构以利用jQuery的排序。您可以将其用作临时排序机制。这是一个袖手旁观的事情:

function putInDocumentOrder(a) {
      var elements;

    // Get elements in order and remember the index of
    // the entry in `a`
    elements = $().add(a.map(function(entry, index){
        entry.element.__index = index;
        return entry.element;
    }));

    // Build array of entries in element order
    a = elements.map(function(){
        return a[this.__index];
    }).get();

    return a;
}

它需要一个expando,但它可以完成任务。 Live Example

这样做的好处是它适用于jQuery支持的所有浏览器,而不是你自己处理边缘情况(例如IE8不支持compareDocumentPosition)。

答案 2 :(得分:1)

虽然我不建议你采用这种方法,但jquery可以获得“全局DOM索引”:

var $all = $("*");

// use index to get your element's index within the entire DOM
$all.index($YOUR_EL);

答案 3 :(得分:0)

如果您可以假设页面上的可见位置相同,那么这可能是一个相对快速的解决方案:

function compareByVisibleLocation(a, b) {
  if (a.offsetTop > b.offsetTop) {
    return 1;
  } else if (a.offsetTop < b.offsetTop) {
    return -1;
  }

  if (a.offsetLeft > b.offsetLeft) {
    return 1;
  } else if (a.offsetLeft < b.offsetLeft) {
    return -1;
  } else {
    return 0;
  }
}

nodes.sort(compareByVisibleLocation);

否则,您可以使用querySelectorAll()快速构建索引(这是深度优先的预先遍序遍历),方法是使用indexed-node类名明确标记要编制索引的节点。

var nodes = document.querySelectorAll('.indexed-node');

或者通过抓取所有单个节点类型,如果有效:

var nodes = document.querySelectorAll('div');

或者只是抓住所有节点:

var nodes = document.querySelectorAll('*');

然后为每个节点添加一个索引属性:

for (var i = 0; i < nodes.length; i++) { nodes[i].__DomIndex = i; }

您可以这样排序:

var nodes = yourGetMethod().sort(function(a,b) {
  if (a === b) {
    return 0;
  } else if (a.__DomIndex > b.__DomIndex) {
    return 1;
  } else {
    return -1;
  }
});

应该适用于IE8。