【问题标题】:Chrome extension hides button unexpectedly in YouTubeChrome 扩展程序在 YouTube 中意外隐藏按钮
【发布时间】:2017-01-22 14:44:02
【问题描述】:

我已经构建了一个 Chrome 扩展程序,它在初始页面加载时有意隐藏缩略图,效果很好。但它也会在单击一次后隐藏“加载更多”按钮并阻止加载更多缩略图,即使还有更多的视频/缩略图需要加载。

这是我点击Load more后执行的部分扩展代码,然后隐藏了Load more按钮:

var insertedNodes = [];
var observer = new MutationObserver(function (mutations) {
    mutations.forEach(function (mutation) {
        // loop through each chunk of html that changes on the page
        for (var i = 0; i < mutation.addedNodes.length; i++){
            newHTMLChunk = mutation.addedNodes[i];

            if (!newHTMLChunk.tagName) 
                continue;

            qHTML = $('<div/>').html(newHTMLChunk).contents();
            var vID = qHTML.find('div').attr("data-context-item-id");

            if(vID){
                console.log("dynamic vID: " + vID);
                qHTML.find('img').hide();
            }
        }
    })
});
observer.observe(document, {
    attributes: true,
    childList: true,
    characterData: true,
    subtree:true
});

这是初始页面加载后的样子,我的扩展程序正在运行:

单击加载更多按钮后,请注意按钮消失并且不再加载拇指:

这是 YouTube 的 Load more 按钮的 html,它似乎没有任何可以匹配 img 选择器以隐藏的内容:

<span class="yt-uix-button-content">
    <span class="load-more-loading hid">
        <span class="yt-spinner">
            <span class="yt-spinner-img  yt-sprite" title="Loading icon"></span>       
            Loading...
        </span>
    </span>
    <span class="load-more-text">
        Load more
    </span>
</span>

这是newHTMLChunk.innerHTML 的内容示例(请注意,只有 1 个 img 元素,这是我想要隐藏的元素,但不隐藏“加载更多”按钮,也不会阻止加载更多拇指):

<div class="yt-lockup clearfix  yt-lockup-video yt-lockup-grid vve-check" data-context-item-id="n3Qb30awpLs" data-visibility-tracking="QLvJwrX0-4a6nwE=">
<div class="yt-lockup-dismissable">
    <div class="yt-lockup-thumbnail">
        <span class=" spf-link  ux-thumb-wrap contains-addto">
            <a href="/watch?v=n3Qb30awpLs" class="yt-uix-sessionlink" aria-hidden="true" data-sessionlink="ei=PdyEWNiaGozB-AOT64qQCA&amp;feature=c4-videos-u">
                <span class="video-thumb  yt-thumb yt-thumb-196">
                    <span class="yt-thumb-default">
                        <span class="yt-thumb-clip">
                            <img onload=";__ytRIL(this)" alt="" aria-hidden="true" width="196" src="https://i.ytimg.com/vi/n3Qb30awpLs/hqdefault.jpg?custom=true&amp;w=336&amp;…amp;jpg444=true&amp;jpgq=90&amp;sp=68&amp;sigh=CiLl7jzBOIIFewRt-KAOEs-bKwA" data-ytimg="1">
                                <span class="vertical-align"></span>
                            </span>
                        </span>
                    </span>
                </a>
                <span class="video-time" aria-hidden="true">
                    <span aria-label="103 seconds">1:43</span>
                </span>
                <span class="thumb-menu dark-overflow-action-menu video-actions">
                    <button class="yt-uix-button-reverse flip addto-watch-queue-menu spf-nolink hide-until-delayloaded yt-uix-button yt-uix-button-dark-overflow-action-menu yt-uix-button-size-default yt-uix-button-has-icon no-icon-markup yt-uix-button-empty" type="button" aria-haspopup="true" aria-expanded="false" onclick=";return false;">
                        <span class="yt-uix-button-arrow yt-sprite"></span>
                        <ul class="watch-queue-thumb-menu yt-uix-button-menu yt-uix-button-menu-dark-overflow-action-menu hid">
                            <li role="menuitem" class="overflow-menu-choice addto-watch-queue-menu-choice addto-watch-queue-play-next yt-uix-button-menu-item" data-action="play-next" onclick=";return false;" data-video-ids="n3Qb30awpLs">
                                <span class="addto-watch-queue-menu-text">Play next</span>
                            </li>
                            <li role="menuitem" class="overflow-menu-choice addto-watch-queue-menu-choice addto-watch-queue-play-now yt-uix-button-menu-item" data-action="play-now" onclick=";return false;" data-video-ids="n3Qb30awpLs">
                                <span class="addto-watch-queue-menu-text">Play now</span>
                            </li>
                        </ul>
                    </button>
                </span>
                <button class="yt-uix-button yt-uix-button-size-small yt-uix-button-default yt-uix-button-empty yt-uix-button-has-icon no-icon-markup addto-button video-actions spf-nolink hide-until-delayloaded addto-watch-later-button yt-uix-tooltip" type="button" onclick=";return false;" role="button" title="Watch Later" data-video-ids="n3Qb30awpLs"></button>
                <button class="yt-uix-button yt-uix-button-size-small yt-uix-button-default yt-uix-button-empty yt-uix-button-has-icon no-icon-markup addto-button addto-queue-button video-actions spf-nolink hide-until-delayloaded addto-tv-queue-button yt-uix-tooltip" type="button" onclick=";return false;" title="Queue" data-video-ids="n3Qb30awpLs" data-style="tv-queue"></button>
            </span>
        </div>
        <div class="yt-lockup-content">
            <h3 class="yt-lockup-title ">
                <a class="yt-uix-sessionlink yt-uix-tile-link  spf-link  yt-ui-ellipsis yt-ui-ellipsis-2" dir="ltr" title="Josh’s Afterlife Analogy" aria-describedby="description-id-407587" data-sessionlink="ei=PdyEWNiaGozB-AOT64qQCA&amp;feature=c4-videos-u" href="/watch?v=n3Qb30awpLs">Josh’s Afterlife Analogy</a>
                <span class="accessible-description" id="description-id-407587"> - Duration: 103 seconds.</span>
            </h3>
            <div class="yt-lockup-meta">
                <ul class="yt-lockup-meta-info">
                    <li>16,475 views</li>
                    <li>2 months ago</li>
                </ul>
            </div>
        </div>
    </div>
    <div class="yt-lockup-notifications-container hid" style="height:110px"></div>
</div>

当我在 Chrome 的调试器中逐步执行代码时,我发现每个新加载的缩略图周围的整个 div 都被隐藏了,而不仅仅是图像。所以,事实上,额外的拇指正在加载,但是整个 div 被隐藏(不仅仅是 img 元素),所有后续 div 一个接一个地向左滑动,直到看起来没有新加载。即使该按钮中没有图像,最终的 div hide 实际上也隐藏了该按钮。为什么要这样做?

如何更改此设置,以便 (1) 附加视频缩略图按预期加载和显示,并且仅隐藏内部的 img 元素; (2) Load more 按钮一直可见,直到真的没有更多的拇指要加载?

【问题讨论】:

  • 设置断点并正确调试代码。顺便说一句,您的 MutationObserver 回调效率非常低。
  • 如何提高效率?
  • 只观察你需要的东西,不要在里面使用 jQuery,不要重新创建 html 块,and so on。无论如何,这是一个单独的问题,一旦您解决了主要问题,您就可以继续解决。
  • 注意一些添加的节点是文本节点(不是 html 元素),所以首先检查类型:if (!newHTMLChunk.tagName) continue; 您可能需要额外检查 DocumentFragment。
  • 感谢 wOxxOm,我已经删除了一个有问题的行并正在调试。请参阅上面的编辑。

标签: javascript google-chrome-extension youtube


【解决方案1】:

MutationObserver 回调最可能的问题是 jQuery .html(DOMnode) 方法提供的 DOM 节点从页面文档移动到分离(未渲染)节点 qHTML

此外,为了不减慢页面加载速度,MutationObserver 回调必须非常快,
Performance of MutationObserver to detect nodes in entire DOM
也就是说,只在你想要的元素上使用 jQuery,而不是在每个添加的节点上。

onElementAdded('div[data-context-item-id]', document, function(elements) {
    elements.forEach(function(element) {
        var vID = element.dataset.contextItemId;
        if (vID) {
            console.log("dynamic vID: " + vID);
            $(element).find('img').hide();
        }
    });
});

function onElementAdded(selector, baseNode, callback) {
    new MutationObserver(function(mutations) {
        var found = [];
        for (var mutation, i = 0; (mutation = mutations[i++]); ) {
            var addedNodes = mutation.addedNodes[i];
            for (var node, j = 0; (node = addedNodes[j++]); ) {
                // skip non-element nodes
                if (!node.tagName) {
                    continue;
                }
                // does the added node itself match the selector?
                if (node.matches(selector)) {
                    found.push(node);
                    continue;
                }
                // does the added node contain child elements matching the selector?
                // (since qS is much faster than qSA it makes sense to use it first)
                if (node.querySelector(selector)) {
                    var children = node.querySelectorAll(selector);
                    // prevent stack overflow
                    if (children.length < 1000) {
                        Array.prototype.push.apply(found, children);
                    } else {
                        found = found.concat(children);
                    }
                }
            }
        }
        if (found.length) {
            callback(found);
        }
    }).observe(baseNode || document, {
        childList: true,
        subtree: true,
    });
}

FWIW,如果目标只是将图像隐藏在具有 data-context-item-id 属性的 div 元素中,那么您可以通过简单的一次性 CSS 样式注入来实现(在 manifest.json 中或通过手动添加&lt;style&gt; 元素):div[data-context-item-id] img { display: none; }

【讨论】:

  • 不,我需要一次做一个,因为我还需要为 ajax 调用捕获 youtube id。
【解决方案2】:

在 wOxxOm 的 cmets 的帮助下,我用下面的方法解决了这个问题(但不是发布的答案)。它现在可以完美运行,并且至少效率更高。 wOxxOm,我赞成您的回答,我仍然欢迎您拥有的任何 cmets。我尝试了您的 .observe() 更改,但它似乎不起作用,但我可能做错了。

var insertedNodes = [];
var observer = new MutationObserver(function (mutations) {
    mutations.forEach(function (mutation) {
        // loop through each chunk of html that changes on the page
        for (var i = 0; i < mutation.addedNodes.length; i++){
            newHTMLChunk = mutation.addedNodes[i];

            if (!newHTMLChunk.tagName) 
                continue;

            if(newHTMLChunk.getElementsByTagName('div').length > 0){
                if(newHTMLChunk.getElementsByTagName('div')[0]){
                    var vID = newHTMLChunk.getElementsByTagName('div')[0].getAttribute('data-context-item-id');

                    if(vID){
                        $("div[data-context-item-id='" + vID + "'] img").hide();
                    }
                }
            }
        }
    })
});
observer.observe(document, {
    attributes: true,
    childList: true,
    characterData: true,
    subtree:true
});

【讨论】:

    猜你喜欢
    • 2019-07-12
    • 2016-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-09
    相关资源
    最近更新 更多