如何在页面上加载时更改HTML内容 [英] How to change the HTML content as it's loading on the page

查看:505
本文介绍了如何在页面上加载时更改HTML内容的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在我们的网站上进行A / B测试,而且我的大部分工作都是在一个JS文件中,该文件在呈现任何其他内容之前加载到页面顶部但在jQuery加载之后有时会派上用场。

I do A/B Testing on our site and I do most of my work is in a JS file that is loaded at the top of the page before anything else is rendered but after jQuery has loaded which comes in handy at times.

举一个非常简单的改变H1标签的例子,我通常会在头部注入一个样式来将H1不透明度设置为0,然后在DOMContentLoaded上,我会操纵H1内容然后将不透明度设置为1.这样做的原因是为了避免在更改发生之前闪现旧内容 - 隐藏整个对象在眼睛上更优雅。

Taking a very simple example of changing an H1 tag, I would normally inject a style in the head to set the H1 opacity to 0 and then on DOMContentLoaded, I would manipulate the H1 contents and then set the opacity to 1. The reason for this is to avoid a flash of the old content before the change takes place - hiding the whole object is more graceful on the eye.

我开始研究MutationObserver API。我之前在使用覆盖对话框中更改内容时使用过这个用户可以打开的内容,这似乎是一个非常酷的方法,我想知道是否有人设法使用MutationObserver来收听文件,因为它是第一次加载/在第一次渲染之前和DOMContentLoaded之前解析并对文档进行更改?

I've started to look at the MutationObserver API. I've used this before when changing content in an overlay dialog box that the user could open which seems to be quite a cool approach and I'm wondering if anyone has managed to use a MutationObserver to listen to the document as it's first loading/ parsing and make changes to the document before first render and before DOMContentLoaded?

这种方法可以让我更改H1内容而不必隐藏它,更改它,然后显示它。

This approach would then let me change the H1 content without having to hide it, change it, then show it.

我尝试但到目前为止失败了,刚刚结束了关于即将发生的变异事件的阅读,并想知道我是否正在尝试做某事这是不可能的。然而,我们(不是我)设法将机器人放在火星上,所以我希望我能解决这个问题。

I've attempted but failed so far and have just ended up reading about the to-be-obselete Mutation Events and wondering if I'm trying to do something that just isn't possible. However we've (not me) have managed to put a robot on Mars so I'm hoping I can solve this.

因此可以使用MutationObservers来改变正在加载/解析页面时,HTML内容是否正在运行?

So is it possible to use MutationObservers to change the HTML content on-the-fly as the page is being loaded/ parsed?

感谢您提供任何帮助或任何指示。

Thanks for any help or any pointers.

问候,
尼克

推荐答案

MDN上的文档有一个通用的不完整示例,并没有展示常见的陷阱。
变异摘要库提供了一个人性化的包装器,但像所有包装器一样,它增加了开销。
请参阅 MutationObserver的性能以检测整个DOM中的节点

The docs on MDN have a generic incomplete example and don't showcase the common pitfalls. Mutation summary library provides a human-friendly wrapper, but like all wrappers it adds overhead. See Performance of MutationObserver to detect nodes in entire DOM.

创建并启动观察者。

让我们使用递归文档范围的MutationObserver来报告所有添加/删除的节点。

Let's use a recursive document-wide MutationObserver that reports all added/removed nodes.

var observer = new MutationObserver(onMutation);
observer.observe(document, {
    childList: true, // report added/removed nodes
    subtree: true,   // observe any descendant elements
});

添加节点的天真枚举。

减慢大型/复杂页面的加载速度,请参阅性能

有时候错过在父容器中合并的H1元素,请参阅下一节。

Slows down loading of enormously big/complex pages, see Performance.
Sometimes misses the H1 elements coalesced in parent container, see the next section.

function onMutation(mutations) {
    mutations.forEach(mutation, mutation =>
        Array.prototype
            .filter.call(mutation.addedNodes, added =>
                added.localName == 'h1' && added.textContent.match(/foo/)
            ).forEach(h1 =>
                h1.innerHTML = h1.innerHTML.replace(/foo/, 'bar')
            );
    );
}

添加节点的高效枚举。

现在很难。在加载页面时,变异记录中的节点可能是容器(如整个站点标题块,其所有元素仅报告为一个添加的节点):规范不要求每个添加的节点单独列出,因此我们必须使用 querySelectorAll (非常慢)或 getElementsByTagName (非常快)。

Now the hard part. Nodes in a mutation record may be containers while a page is being loaded (like the entire site header block with all its elements reported as just one added node): the specification doesn't require each added node to be listed individually, so we'll have to look inside each element using querySelectorAll (extremely slow) or getElementsByTagName (extremely fast).

function onMutation(mutations) {
    for (var i = 0, len = mutations.length; i < len; i++) {
        var added = mutations[i].addedNodes;
        for (var j = 0, lenAdded = added.length; j < lenAdded; j++) {
            var node = added[j];
            var found;
            if (node.localName === 'h1') {
                found = [node];
            } else if (node.children && node.children.length) {
                found = node.getElementsByTagName('h1');
            } else {
                continue;
            }
            for (var k = 0, lenFound = found.length; k < lenFound; k++) {
                var h1 = found[k];
                if (!h1.parentNode || !h1.textContent.match(/foo/)) {
                    // either removed in another mutation or has no text to replace
                    continue;
                }
                var walker = document.createTreeWalker(h1, NodeFilter.SHOW_TEXT);
                while (walker.nextNode()) {
                    var textNode = walker.currentNode;
                    var text = textNode.nodeValue;
                    if (text.match(/foo/)) {
                        textNode.nodeValue = text.replace(/foo/, 'bar');
                    }
                }
            }
        }
    }
}

为什么丑陋的香草循环?因为 forEach 过滤器和ES2015 for(数组的val)相比之下非常缓慢。请参阅 MutationObserver的性能以检测整个DOM中的节点

Why the ugly vanilla for loops? Because forEach and filter and ES2015 for (val of array) are very slow in comparison. See Performance of MutationObserver to detect nodes in entire DOM.

为什么 TreeWalker ?保留附加到子元素的任何事件侦听器。要仅更改 Text 节点:它们没有子节点,更改它们不会触发新的突变,因为我们使用了 childList :true ,而不是 characterData:true

Why the TreeWalker? To preserve any event listeners attached to sub-elements. To change only the Text nodes: they don't have child nodes, and changing them doesn't trigger a new mutation because we've used childList: true, not characterData: true.

处理相对稀有的元素通过实时HTMLCollection而不枚举突变。

所以我们寻找一个很少使用的元素,如H1标签或IFRAME等。我们可以使用getElementsByTagName返回的自动更新的HTMLCollection来简化和加速观察者回调。

So we look for an element that is supposed to be used rarely like H1 tag, or IFRAME, etc. In this case we can simplify and speed up the observer callback with an automatically updated HTMLCollection returned by getElementsByTagName.

var h1s = document.getElementsByTagName('h1');
function onMutation(mutations) {
    if (mutations.length == 1) {
        // optimize the most frequent scenario: one element is added/removed
        var added = mutations[0].addedNodes[0];
        if (!added || (added.localName !== 'h1' && !added.children.length)) {
            // so nothing was added or non-H1 with no child elements
            return;
        }
    }
    // H1 is supposed to be used rarely so there'll be just a few elements
    for (var i = 0, len = h1s.length; i < len; i++) {
        var h1 = h1s[i];
        if (!h1.textContent.match(/foo/)) {
            continue;
        }
        var walker = document.createTreeWalker(h1, NodeFilter.SHOW_TEXT);
        while (walker.nextNode()) {
            var textNode = walker.currentNode;
            var text = textNode.nodeValue;
            if (text.match(/foo/)) {
                textNode.nodeValue = text.replace(/foo/, 'bar');
            }
        }
    }
}

这篇关于如何在页面上加载时更改HTML内容的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆