Javascript 如何将节点数组转换为静态节点列表?

Javascript 如何将节点数组转换为静态节点列表?,javascript,dom,nodelist,Javascript,Dom,Nodelist,注意:在假设这个问题是重复的之前,在这个问题的底部有一个部分说明了为什么一些类似的问题不能提供我想要的答案 我们都知道,将节点列表转换为数组很容易,有很多方法: [].slice.call(someNodeList) // or Array.from(someNodeList) // etc... 我所追求的恰恰相反如何将节点数组转换为静态节点列表? 我为什么要这样做? 在不太深入的情况下,我创建了一个新方法来查询页面上的元素,即: Document.prototype.customQue

注意:在假设这个问题是重复的之前,在这个问题的底部有一个部分说明了为什么一些类似的问题不能提供我想要的答案


我们都知道,将节点列表转换为数组很容易,有很多方法:

[].slice.call(someNodeList)
// or
Array.from(someNodeList)
// etc...
我所追求的恰恰相反如何将节点数组转换为静态节点列表?


我为什么要这样做? 在不太深入的情况下,我创建了一个新方法来查询页面上的元素,即:

Document.prototype.customQueryMethod = function (...args) {...}
为了了解
querySelectorAll
的工作原理,我希望返回一个而不是一个数组


到目前为止,我已经用三种不同的方式处理了这个问题:

尝试1: 创建文档片段 虽然这会返回节点列表,但这不起作用,因为调用
appendChild
会将节点从其在DOM中的当前位置(它应该停留的位置)移除

另一种变体是
克隆
节点并返回克隆。但是,现在返回克隆的节点,这些节点没有对DOM中实际节点的引用


尝试2: 试图“模拟”节点列表构造函数 它将以以下方式使用:

let nodeList = new FakeNodeList(nodes);

// The following tests/uses all work
nodeList instanceOf NodeList // true
nodeList[0] // would return an element
nodeList.item(0) // would return an element
虽然此特定方法不会从DOM中删除元素,但会导致其他错误,例如将其转换为数组时:

let arr = [].slice.call(nodeList);
// or
let arr = Array.from(nodeList);
上述每一项都会产生以下错误:
uncaughttypeerror:invalical invocation

我还试图避免用假节点列表构造函数“模仿”节点列表,因为我相信这可能会在将来产生意想不到的后果


尝试3: 将临时属性附加到元素以重新查询它们 这很有效,直到我发现它对某些元素不起作用,比如
SVG
。它不会附加属性(尽管我只在Chrome中测试过)


看起来这应该是一件容易的事情,为什么我不能使用NodeList构造函数来创建NodeList,为什么我不能以类似于NodeLists转换为数组的方式将数组转换为NodeList

如何以正确的方式将节点数组转换为节点列表?
类似问题的答案对我不适用:

以下问题与此类似。不幸的是,由于以下原因,这些问题/答案无法解决我的特定问题

此问题的答案使用克隆节点的方法。这将不起作用,因为我需要访问原始节点

使用文档片段方法(尝试1)。其他答案在尝试2、3时尝试了类似的事情

正在使用
E4X
,因此不适用。即使它正在使用它,它仍然会从DOM中删除元素。

这是我的两分钱:

  • 文档是本机对象,扩展它可能不是一个好主意
  • NodeList是一个本机对象,具有私有构造函数,没有用于添加元素的公共方法,这肯定是有原因的
  • 除非有人能够提供hack,否则在不修改当前文档的情况下,无法创建和填充节点列表
  • NodeList类似于一个数组,但其
    item
    方法的工作原理与使用方括号类似,只是在超出范围时返回
    null
    而不是
    undefined
    。您只需返回一个实现了item方法的数组:
myArray.item=function(e){返回此[e]| | null;}

PS:可能您采取了错误的方法,您的自定义查询方法可能只是包装了一个
文档。querySelectorAll
调用返回您要查找的内容

为什么我不能使用NodeList构造函数来创建NodeList

由于未指定,因此无法直接在用户脚本中创建

为什么我不能以与节点列表转换为数组类似的方式将数组转换为节点列表呢

在您的情况下,这当然是一个有用的函数,但是DOM规范中没有指定存在这样的函数。因此,不可能从
节点
s的数组直接填充
节点列表

虽然我严重怀疑您是否会将此称为“正确的方法”,但一个丑陋的解决方案是找到唯一选择所需元素的CSS选择器,并将所有这些路径作为逗号分隔的选择器传递到
querySelectorAll

// find a CSS path that uniquely selects this element
function buildIndexCSSPath(elem) {
    var parent = elem.parentNode;

     // if this is the root node, include its tag name the start of the string
    if(parent == document) { return elem.tagName; } 

    // find this element's index as a child, and recursively ascend 
    return buildIndexCSSPath(parent) + " > :nth-child(" + (Array.prototype.indexOf.call(parent.children, elem)+1) + ")";
}

function toNodeList(list) {
    // map all elements to CSS paths
    var names = list.map(function(elem) { return buildIndexCSSPath(elem); });

    // join all paths by commas
    var superSelector = names.join(",");

    // query with comma-joined mega-selector
    return document.querySelectorAll(superSelector);
}

toNodeList([elem1, elem2, ...]);
这是通过查找CSS字符串来唯一地选择每个元素,其中每个选择器的形式为
html>:第n个子(x)>:第n个子(y)>:第n个子(z)…
。也就是说,每个元素都可以理解为作为子元素(等)的子元素的子元素存在,一直到根元素。通过查找节点祖先路径中每个子节点的索引,我们可以唯一地标识它

请注意,这不会保留
文本
类型的节点,因为
querySelectorAll
(以及通常的CSS路径)无法选择文本节点


不过,我不知道这是否足以满足您的需要。

因为从阵列创建真正的节点列表似乎有严重的回退,也许您可以使用带有自制原型的常规JS对象来模拟节点列表。像这样:

var nodeListProto = Object.create({}, {
        item: {
            value: function(x) {
                return (Object.getOwnPropertyNames(this).indexOf(x.toString()) > -1) ? this[x] : null;
            },
            enumerable: true
        },
        length: {
            get: function() {
                return Object.getOwnPropertyNames(this).length;
            },
            enumerable: true
        }
    }),
    getNodeList = function(nodes) {
        var n, eN = nodes.length,
            list = Object.create(nodeListProto);
        for (n = 0; n < eN; n++) { // *
            Object.defineProperty(list, n.toString(), {
                value: nodes[n],
                enumerable: true
            });
        }
        return list;
    };
// Usage:
var nodeListFromArray = getNodeList(arrayOfNodes);
var nodeListProto=Object.create({}{
项目:{
值:函数(x){
return(Object.getOwnPropertyNames(this.indexOf(x.toString())>-1)?this[x]:null;
},
可枚举:true
},
长度:{
get:function(){
返回Object.getOwnPropertyNames(this).length;
},
可枚举:true
}
}),
getNodeList=函数(节点){
function createNodeList(arrayOfNodes) {
    arrayOfNodes.forEach((node) => {
        node.setAttribute('QUERYME', '');
    });
    let nodeList = document.querySelectorAll('[QUERYME]');
    arrayOfNodes.forEach((node) => {
        node.removeAttribute('QUERYME');
    });
    return nodeList;
}
// find a CSS path that uniquely selects this element
function buildIndexCSSPath(elem) {
    var parent = elem.parentNode;

     // if this is the root node, include its tag name the start of the string
    if(parent == document) { return elem.tagName; } 

    // find this element's index as a child, and recursively ascend 
    return buildIndexCSSPath(parent) + " > :nth-child(" + (Array.prototype.indexOf.call(parent.children, elem)+1) + ")";
}

function toNodeList(list) {
    // map all elements to CSS paths
    var names = list.map(function(elem) { return buildIndexCSSPath(elem); });

    // join all paths by commas
    var superSelector = names.join(",");

    // query with comma-joined mega-selector
    return document.querySelectorAll(superSelector);
}

toNodeList([elem1, elem2, ...]);
var nodeListProto = Object.create({}, {
        item: {
            value: function(x) {
                return (Object.getOwnPropertyNames(this).indexOf(x.toString()) > -1) ? this[x] : null;
            },
            enumerable: true
        },
        length: {
            get: function() {
                return Object.getOwnPropertyNames(this).length;
            },
            enumerable: true
        }
    }),
    getNodeList = function(nodes) {
        var n, eN = nodes.length,
            list = Object.create(nodeListProto);
        for (n = 0; n < eN; n++) { // *
            Object.defineProperty(list, n.toString(), {
                value: nodes[n],
                enumerable: true
            });
        }
        return list;
    };
// Usage:
var nodeListFromArray = getNodeList(arrayOfNodes);
function getNodeList(elements) {
  const parentElement = document.createElement('div');
  // This can be a differnet element type, too (but only block (display: block;) element, because it impossible to put block element in inline element, and maybe 'elements' array contains a block element).
  let HTMLString = '';
  for (let element of elements) {
    HTMLString += element.outerHTML;
  }

  parentElement.innerHTML = HTMLString;

  return parentElement.childNodes;
}