简体   繁体   English

使用 jQuery 进行异步 DOM 树遍历

[英]Asynchronous DOM tree traversal with jQuery

I would like to perform a long running, node-by-node walk of the DOM, calling a function on each node, but without making the browser unresponsive.我想对 DOM 执行长时间运行的逐节点遍历,在每个节点上调用一个函数,但不会使浏览器无响应。 So I'm thinking asynchronous is the way to go.所以我认为异步是要走的路。

I think jQuery Deferred objects could provide a solution, but I haven't managed to come up with one yet.我认为 jQuery Deferred 对象可以提供一种解决方案,但我还没有想出一个解决方案。

Can anyone give an example of how you could do this with jQuery (or another library, if one particularly stands out as suitable; or pure Javascript and DOM methods).任何人都可以举例说明如何使用 jQuery(或其他库,如果一个特别适合的库;或纯 Javascript 和 DOM 方法)来执行此操作。

To further complicate things, I would like the ability to traverse in different orders, for example postorder, but not yet a requirement.更复杂的是,我希望能够以不同的顺序遍历,例如后序,但还不是必需的。

You could do this recursively with setTimeout.您可以使用 setTimeout 递归地执行此操作。 Since Javascript is single threaded you can create the illusion of having multiple threads by relinquishing the thread periodically.由于 Javascript 是单线程的,因此您可以通过定期放弃线程来创建具有多个线程的错觉。 Calling setTimeout will queue the remaining work and process any outstanding events before continuing.调用 setTimeout 将对剩余的工作进行排队,并在继续之前处理所有未完成的事件。

Here's a working example with jQuery:这是一个使用 jQuery 的工作示例:

function traverse(node, visitor, root) {
    if (root === undefined)
        root = node;

    visitor(node, function() {
        if (!node.length)
            return;

        let nextNode;
        if (node.children().length) {
            nextNode = node.children().first();
        } else {
            nextNode = node;
            do {
                if (root.length && nextNode.length && nextNode[0] == root[0]) {
                    nextNode = $();
                    break;
                } else if (nextNode.next().length) {
                    nextNode = nextNode.next();
                    break;
                } else {
                    nextNode = nextNode.parent();
                }
            } while (nextNode.length);
        }

        setTimeout(function() { traverse(nextNode, visitor, root); }, 200);
    });
}

This will call visitor with all DOM nodes in depth-first pre-order traversal, then call visitor with an empty jQuery object to indicate that it's done.这将调用visitor在深度优先前序遍历所有DOM节点,然后调用visitor有一个空的jQuery对象,以表明它的完成。 visitor must call its second parameter to proceed with traversal: visitor必须调用它的第二个参数才能继续遍历:

traverse($('#rootNode'), function(node, proceed) {
    if (!node.length) {
        console.log('done with traversal');
        // no proceed() call at the end
    } else if (node.hasClass('my-class')) {
        // let's say that this click triggers an AJAX request that makes a node visible later in traversal
        node.trigger('click');
        proceed(); // continue traversal
    } else if (node.hasClass('ajax-response-container')) {
        function checkIfVisible() {
            if (node.is(':visible')) {
                // yay, ajax response arrived
                console.log('AJAX response', node.html());
                // continue traversal
                proceed();
            } else {
                // wait for ajax node to become visible
                window.requestAnimationFrame(checkIfVisible);
                // no proceed() call - don't continue traversal yet
            }
        }

        checkIfVisible();
    } else {
        // continue traversal for any other node encountered
        proceed();
    }
});

您可以查看https://github.com/wilsonpage/fastdom/ ,它正是这样做的,dom 中的异步操作。

You may want to check this approach您可能想检查这种方法

https://gist.github.com/stelf/4b6935071447724c7066 https://gist.github.com/stelf/4b6935071447724c7066

based on Kris Kowal's Q library.基于 Kris Kowal 的 Q 库。 Basically similar to SynXsiS's idea, but a bit more structured in terms of Promises/Deffered objects.基本上类似于 SynXsiS 的想法,但在 Promises/Deffered 对象方面更加结构化。

If you can use HTML5, some browsers have support for web workers.如果您可以使用 HTML5,则某些浏览器支持 Web Worker。 https://developer.mozilla.org/En/Using_web_workers This may be an option. https://developer.mozilla.org/En/Using_web_workers这可能是一个选项。 You may be able to pass the DOM as document to the methods and go from there.您可以将 DOM 作为文档传递给方法并从那里开始。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM