草庐IT

javascript - 如何将节点数组转换为静态 NodeList?

coder 2023-07-05 原文

注意:在假定此问题重复之前,此问题底部有一个部分说明了为什么一些类似的问题没有提供我正在寻找的答案。


我们都知道将 NodeList 转换为 Array 很容易,并且有很多方法可以做到这一点:

[].slice.call(someNodeList)
// or
Array.from(someNodeList)
// etc...

我追求的是相反的; 如何将节点数组转换为静态 NodeList?


我为什么要这样做?

在不深入的情况下,我正在创建一种新方法来查询页面上的元素,即:

Document.prototype.customQueryMethod = function (...args) {...}

为了忠实于 querySelectorAll 的工作原理,我想返回一个 static collection NodeList而不是数组。


到目前为止,我已经通过三种不同的方式解决了这个问题:

尝试 1:

创建文档片段

function createNodeList(arrayOfNodes) {
    let fragment = document.createDocumentFragment();
    arrayOfNodes.forEach((node) => {
        fragment.appendChild(node);
    });
    return fragment.childNodes;
}

虽然这确实会返回一个 NodeList,但这不起作用,因为调用 appendChild 会将节点从其在 DOM 中的当前位置(它应该停留的位置)移除。

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


尝试 2:

试图“模拟”NodeList 构造函数

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

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


尝试 3:

将临时属性附加到元素以重新查询它们

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 ?

如何以正确的方式将节点数组转换为 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/

有关javascript - 如何将节点数组转换为静态 NodeList?的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

    我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

  4. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  5. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  6. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  7. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  8. ruby-on-rails - 在 Ruby 中循环遍历多个数组 - 2

    我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代

  9. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  10. ruby - 多次弹出/移动 ruby​​ 数组 - 2

    我的代码目前看起来像这样numbers=[1,2,3,4,5]defpop_threepop=[]3.times{pop有没有办法在一行中完成pop_three方法中的内容?我基本上想做类似numbers.slice(0,3)的事情,但要删除切片中的数组项。嗯...嗯,我想我刚刚意识到我可以试试slice! 最佳答案 是numbers.pop(3)或者numbers.shift(3)如果你想要另一边。 关于ruby-多次弹出/移动ruby​​数组,我们在StackOverflow上找到一

随机推荐