- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
注意:在假定此问题重复之前,此问题底部有一个部分说明了为什么一些类似的问题没有提供我正在寻找的答案。
我们都知道将 NodeList 转换为 Array 很容易,并且有很多方法可以做到这一点:
[].slice.call(someNodeList)
// or
Array.from(someNodeList)
// etc...
我追求的是相反的; 如何将节点数组转换为静态 NodeList?
在不深入的情况下,我正在创建一种新方法来查询页面上的元素,即:
Document.prototype.customQueryMethod = function (...args) {...}
为了忠实于 querySelectorAll
的工作原理,我想返回一个 static collection NodeList
而不是数组。
到目前为止,我已经通过三种不同的方式解决了这个问题:
function createNodeList(arrayOfNodes) {
let fragment = document.createDocumentFragment();
arrayOfNodes.forEach((node) => {
fragment.appendChild(node);
});
return fragment.childNodes;
}
虽然这确实会返回一个 NodeList,但这不起作用,因为调用 appendChild
会将节点从其在 DOM 中的当前位置(它应该停留的位置)移除。
另一种变体涉及克隆
节点并返回克隆。但是,现在您要返回克隆的节点,这些节点没有引用 DOM 中的实际节点。
const FakeNodeList = (() => {
let fragment = document.createDocumentFragment();
fragment.appendChild(document.createComment('create a nodelist'));
function NodeList(nodes) {
let scope = this;
nodes.forEach((node, i) => {
scope[i] = node;
});
}
NodeList.prototype = ((proto) => {
function F() {
}
F.prototype = proto;
return new F();
})(fragment.childNodes);
NodeList.prototype.item = function item(idx) {
return this[idx] || null;
};
return NodeList;
})();
它将以下列方式使用:
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);
以上每个都会产生以下错误:Uncaught TypeError: Illegal invocation
我还试图避免使用伪造的节点列表构造函数“模仿”节点列表,因为我认为这可能会在未来产生意想不到的后果。
function createNodeList(arrayOfNodes) {
arrayOfNodes.forEach((node) => {
node.setAttribute('QUERYME', '');
});
let nodeList = document.querySelectorAll('[QUERYME]');
arrayOfNodes.forEach((node) => {
node.removeAttribute('QUERYME');
});
return nodeList;
}
这工作得很好,直到我发现它不适用于某些元素,例如 SVG
的。它不会附加属性(尽管我只在 Chrome 中测试过)。
看起来这应该是一件容易的事,为什么我不能使用 NodeList 构造函数来创建一个 NodeList,为什么我不能以类似于 NodeLists 转换为数组的方式将数组转换为 NodeList ?
答案不适合我的类似问题:
以下问题与本题类似。不幸的是,由于以下原因,这些问题/答案无法解决我的特定问题。
How can I convert an Array of elements into a NodeList?这个问题的答案使用了克隆节点的方法。这行不通,因为我需要访问原始节点。
Create node list from a single node in JavaScript使用文档片段方法(尝试 1)。其他答案在尝试 2 和 3 中尝试了类似的事情。
Creating a DOM NodeList正在使用 E4X
,因此不适用。即使它正在使用它,它仍然会从 DOM 中删除元素。
最佳答案
why can't I use the NodeList constructor to create a NodeList
因为 DOM specification for the NodeList
interface未指定 the WebIDL [Constructor] attribute , 所以它不能直接在用户脚本中创建。
why can't I cast an array to a NodeList in a similar fashion that NodeLists are cast to arrays?
在您的情况下,这肯定是一个有用的函数,但 DOM 规范中没有指定存在这样的函数。因此,不可能从 Node
数组直接填充 NodeList
。
虽然我严重怀疑您会称之为“正确的方式”来处理事情,但一个丑陋的解决方案是找到 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 > :nth-child(x) > :nth-child(y) > :nth-child(z) ...
。也就是说,每个元素都可以理解为作为子元素(等等)的子元素一直存在于根元素之上。通过在节点的祖先路径中找到每个 child 的索引,我们可以唯一地标识它。
请注意,这不会保留 Text
类型的节点,因为 querySelectorAll
(和一般的 CSS 路径)无法选择文本节点。
不过,我不知道这是否足以满足您的目的。
关于javascript - 如何将节点数组转换为静态 NodeList?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38440492/
我是一名优秀的程序员,十分优秀!