隐藏具有镀铬延伸的DOM元素而不会引起闪烁

发布于 2025-01-25 01:31:16 字数 8606 浏览 5 评论 0原文

前言:

我知道有一个重复的问题在那里。我再次发布它是因为它没有答案(并且是4年前的)。

我想要的一般描述:

我希望能够隐藏一个dom-Element(添加element.style.display =“ none” none“”)视图。

我尝试过的内容:

其他帖子指向使用突变处理器并在文档元素上运行它。

为了确保我们能够在加载DOM之前隐藏一个元素,我们将运行包含MutationObserver的脚本作为content_script “ run_at”:“ document_start”

我做了所有这一切,但我仍然看到闪烁(当我加载页面然后迅速消失时出现元素)。

我正在尝试做的事情:

有一个ul,其中包含一些li,并在页面上注入我的content_script。 JS进入。我用popup.html带有< text,复选框> pairs。如果检查了复选框,则可以看到包含所述文本的li否则隐藏了。我希望它能在刷新之间持续存在,因此使用存储

事情有效 - 但是每当我刷新页面时都会闪烁。元素在那里,然后它们消失了。我不希望他们首先出现!

我的代码:

当我检测到我可以删除的DOM元素已加载时,我会生成一个对象,该对象表明我是否应该隐藏或保持该特定的DOM元素。 然后,我将其element.Style.display设置为noneblock相应地。

/**manifest.json
...
"content_scripts": [
    {
      "matches": [
        "some_website_url"
      ],
      "js": [
        "content_script.js"
      ],
      "run_at": "document_start"
    }
  ]
...
*/

///content_script.js
const mutationObserver = new MutationObserver((mutations) => {
    for (const { addedNodes } of mutations) {
        for (const node of addedNodes) {
            if (node.tagName) {
                if (node.querySelector(potentially_hidden_element_selector)) {
                    chrome.storage.sync.get("courses", ({ courses }) => {
                        chrome.storage.sync.set({ "courses": generateCourseList(courses) }, () => {
                            const courseElements = Array.from(node.closest('ul').querySelectorAll('a[data-parent-key="mycourses"]'))
                            courseElements.forEach(courseElement => {
                                const courseName = getCourseName(courseElement)
                                const isVisible = courses[courseName]
                                updateCourseElementInSidebar(courseElement, isVisible)
                            })
                        })
                    })
                    // We found what we were looking for so stop searching
                    mutationObserver.disconnect()
                }
            }
        }
    }
})

mutationObserver.observe(document, { childList: true, subtree: true })

编辑1:

我的generatecourselist方法取决于我可能尝试隐藏的dom元素 - 因此我无法调用chrome.storage.set.set.set我认为DOM加载之前的方法。

当我刷新页面时,一门课程列表最终填充了DOM。 然后,我根据这些课程元素的Innertext属性填充存储的课程对象。我将这些元素的可见性设置为truefalse基于两个因素之一:如果在courses中已经定义了此课程对象,保持其可见性状态,如果不是,请将其设置为true(默认情况下可见)。

如果我没有提及,我将无法看到某些DOM元素。因此,如果我尝试调用generateCourSelist在加载这些特定的DOM元素之前,我最终试图检索所有课程元素(document.queryselectorall('a [data-parent-kekey ='a [data-parent-key =“ mycourses”]')),什么也没返回。我最终在chrome.chrome.storage中设置了课程,因为此chrome.storage.storage.sync.set({“ courses”:generatecourselist(courses)}。 。

.. 为了依赖chrome.storage.sync.set的结果

。当我要访问的内容(课程元素)之前,我正在发现一个课程元素,而当我认为这是一个错误。弹出的那一刻,有时候只有6个课程元素被加载了,因为我不能从一个人身上改变。不知道何时断开突变处理器的连接。我使用了调试器,并试图在加载所有6个课程元素后尽快找到哪些元素已加载,这是header#page-header.row.row element。我仍然会闪烁,尽管不如以前引人注目。

我能做的事情使它变得更加明显吗?

function start_mutation_observer() {
    chrome.storage.sync.get({ 'savedCourses': {} }, ({ savedCourses }) => {
        const observer = new MutationObserver((mutations) => {
            for (const { addedNodes } of mutations) {
                for (const node of addedNodes) {
                    // The page header gets updated AFTER the courseList is updated - so once it's in the page, we know the courseElements are too
                    if (document.querySelector('header#page-header.row')) {
                        observer.disconnect()

                        const generatedCourses = generateCourseList(savedCourses)
                        const courseElements = getCourseElements()

                        // Set visibility of course elements 
                        courseElements.forEach(courseElement => {
                            const courseName = getCourseElementTextContent(courseElement);
                            const isShown = generatedCourses[courseName];
                            setCourseElementVisibility(courseElement, isShown);
                        });

                        chrome.storage.sync.set({ 'savedCourses': generatedCourses });
                        return
                    }
                }
            }
        });
        observer.observe(document, { childList: true, subtree: true });

        // In case the content script has been injected when some of the DOM has already loaded
        onMutation([{ addedNodes: [document.documentElement] }]);
    });
}

function getCourseElements() {
    const COURSE_ELEMENT_SELECTOR = 'ul > li > a[data-parent-key="mycourses"]'
    return Array.from(document.querySelectorAll(COURSE_ELEMENT_SELECTOR))
}

function getCourseElementTextContent(courseElement) {
    const COURSE_ELEMENT_TEXT_CONTAINER_SELECTOR = 'a[data-parent-key="mycourses"] > div > div > span.media-body'
    return courseElement.querySelector(COURSE_ELEMENT_TEXT_CONTAINER_SELECTOR).textContent
}

function generateCourseList(savedCourses) {
    // Turns [[a, b], [b,c]] into {a:b, b:c}
    return Object.fromEntries(getCourseElements().map(courseElement => {
        const courseName = getCourseElementTextContent(courseElement)
        const isShown = savedCourses[courseName] ?? true
        return [courseName, isShown]
    }))
}

function setCourseElementVisibility(courseElement, isShown) {
    if (isShown) {
        courseElement.style.display = "block"
    } else {
        courseElement.style.display = "none"
    }
}

start_mutation_observer()

编辑3:

我认为它与现在一样好。我只刷新刚刚加载到DOM的课程元素的可见性。基本上没有闪烁(有一点闪烁,但没有我的扩展,它的闪烁量相同)。

这是突变处理器的代码

function start_mutation_observer() {
    let handledCourseElements = new Set()
    chrome.storage.sync.get({ 'savedCourses': {} }, ({ savedCourses }) => {
        const observer = new MutationObserver((mutations) => {
            for (const { addedNodes } of mutations) {
                for (const node of addedNodes) {
                    const courseElements = getCourseElements()
                    const courseElementsAdded = courseElements.length > handledCourseElements.size
                    // If a courseElement was added, update visibility of those that weren't already processed 
                    if (courseElementsAdded) {
                        const generatedCourses = generateCourseList(savedCourses)
                        courseElements
                            .filter(courseElement => !handledCourseElements.has(courseElement))
                            .forEach(courseElement => {
                                const courseName = getCourseElementTextContent(courseElement)
                                const courseShouldBeVisible = generatedCourses[courseName];
                                setCourseElementVisibility(courseElement, courseShouldBeVisible);
                                handledCourseElements.add(courseElement)
                            })
                    }

                    // The page header gets updated AFTER the courseList is updated - so once it's in the page, we know the courseElements are too
                    if (document.querySelector('header#page-header.row')) {
                        observer.disconnect()
                        chrome.storage.sync.set({ 'savedCourses': generateCourseList(savedCourses) });
                        return
                    }
                }
            }
        });
        observer.observe(document, { childList: true, subtree: true });

        // In case the content script has been injected when some of the DOM has already loaded
        onMutation([{ addedNodes: [document.documentElement] }]);
    });
}

Preface:

I am aware that there is a duplicate question out there. I am posting it again because it has no answers (and it's from 4 years ago).

General description of what I want:

I want to be able to hide a DOM-element (adding Element.style.display = "none") before the DOM is loaded into the view.

What I've tried:

Other posts point to using a MutationObserver and running it on the document element.

To ensure that we are able to hide an element before the DOM is loaded, we are to run the script containing the MutationObserver as a content_script with "run_at":"document_start".

I did all of this, and I still see a flicker (the elements appear when I load a page and then quickly disappear).

What I'm trying to do:

There's a ul which contains some li with some text on the page I inject my content_script.js into. I populate my popup.html with <text, checkbox> pairs. If the checkbox is checked, the li containing said text is visible, else it is hidden. I want it to persist between refreshes, hence the use of storage.

Things work - but there's a flicker whenever I refresh the page. The elements are there, then they're gone. I don't want them to show up in the first place!

My code:

When I detect that the DOM elements I may remove have loaded, I generate an Object that indicates whether I should hide or keep visible that specific DOM element.
I then set its Element.style.display to none or block accordingly.

/**manifest.json
...
"content_scripts": [
    {
      "matches": [
        "some_website_url"
      ],
      "js": [
        "content_script.js"
      ],
      "run_at": "document_start"
    }
  ]
...
*/

///content_script.js
const mutationObserver = new MutationObserver((mutations) => {
    for (const { addedNodes } of mutations) {
        for (const node of addedNodes) {
            if (node.tagName) {
                if (node.querySelector(potentially_hidden_element_selector)) {
                    chrome.storage.sync.get("courses", ({ courses }) => {
                        chrome.storage.sync.set({ "courses": generateCourseList(courses) }, () => {
                            const courseElements = Array.from(node.closest('ul').querySelectorAll('a[data-parent-key="mycourses"]'))
                            courseElements.forEach(courseElement => {
                                const courseName = getCourseName(courseElement)
                                const isVisible = courses[courseName]
                                updateCourseElementInSidebar(courseElement, isVisible)
                            })
                        })
                    })
                    // We found what we were looking for so stop searching
                    mutationObserver.disconnect()
                }
            }
        }
    }
})

mutationObserver.observe(document, { childList: true, subtree: true })

EDIT 1:

My generateCourseList method depends on the DOM elements I may try to hide - so I can't call the chrome.storage.set method before the DOM has loaded I think.

When I refresh the page, a list of courses eventually populates the DOM.
I then populate the storage's courses object based on these course elements' innerText properties. I set each of these elements' visibility to true or false based on one of two factors: if this course is already defined in the courses object, keep its visibility status, if it isn't, set it to true (visible by default).

I can't make certain DOM elements visible/hidden if I don't have reference to them though. So if I try to call generateCourseList before those specific DOM elements have loaded, I end up trying to retrieve all the course elements (document.querySelectorAll('a[data-parent-key="mycourses"]')) and get returned nothing. I end up setting courses in chrome.storage to nothing because of this chrome.storage.sync.set({ "courses": generateCourseList(courses) }....

EDIT 2:

Here is all of my code. I try to chrome.storage.sync.get as soon as I can, and I try to not depend on the result of chrome.storage.sync.set.

I try to delete the elements as soon as I can, but I'm having difficulty doing so. This is because I have difficulty knowing when the content I want to access (the course elements) have fully loaded. Previously, I was detecting when one course element was visible, and when it was, I assumed all were. This was a mistake. I was able to access the one courselement the moment it popped up, but sometimes only 4 of the 6 course elements were actually loaded. I can't hardcode this number, because it changes from person to person. I can't just tackle them one by one, because then I wouldn't know when to disconnect the MutationObserver. I used the debugger and tried to find what element is loaded soon after all 6 course elements are loaded, and that is the header#page-header.row element. I still get a flicker, though less noticeable than before.

Anything I can do to make it even less noticeable?

function start_mutation_observer() {
    chrome.storage.sync.get({ 'savedCourses': {} }, ({ savedCourses }) => {
        const observer = new MutationObserver((mutations) => {
            for (const { addedNodes } of mutations) {
                for (const node of addedNodes) {
                    // The page header gets updated AFTER the courseList is updated - so once it's in the page, we know the courseElements are too
                    if (document.querySelector('header#page-header.row')) {
                        observer.disconnect()

                        const generatedCourses = generateCourseList(savedCourses)
                        const courseElements = getCourseElements()

                        // Set visibility of course elements 
                        courseElements.forEach(courseElement => {
                            const courseName = getCourseElementTextContent(courseElement);
                            const isShown = generatedCourses[courseName];
                            setCourseElementVisibility(courseElement, isShown);
                        });

                        chrome.storage.sync.set({ 'savedCourses': generatedCourses });
                        return
                    }
                }
            }
        });
        observer.observe(document, { childList: true, subtree: true });

        // In case the content script has been injected when some of the DOM has already loaded
        onMutation([{ addedNodes: [document.documentElement] }]);
    });
}

function getCourseElements() {
    const COURSE_ELEMENT_SELECTOR = 'ul > li > a[data-parent-key="mycourses"]'
    return Array.from(document.querySelectorAll(COURSE_ELEMENT_SELECTOR))
}

function getCourseElementTextContent(courseElement) {
    const COURSE_ELEMENT_TEXT_CONTAINER_SELECTOR = 'a[data-parent-key="mycourses"] > div > div > span.media-body'
    return courseElement.querySelector(COURSE_ELEMENT_TEXT_CONTAINER_SELECTOR).textContent
}

function generateCourseList(savedCourses) {
    // Turns [[a, b], [b,c]] into {a:b, b:c}
    return Object.fromEntries(getCourseElements().map(courseElement => {
        const courseName = getCourseElementTextContent(courseElement)
        const isShown = savedCourses[courseName] ?? true
        return [courseName, isShown]
    }))
}

function setCourseElementVisibility(courseElement, isShown) {
    if (isShown) {
        courseElement.style.display = "block"
    } else {
        courseElement.style.display = "none"
    }
}

start_mutation_observer()

EDIT 3:

I think it's as good as can be now. I only refresh the visibility of the course elements that were just loaded into the DOM. There's essentially no flicker now (there is a slight one, but its' the same amount of flickering without my extension).

Here is the code for the MutationObserver

function start_mutation_observer() {
    let handledCourseElements = new Set()
    chrome.storage.sync.get({ 'savedCourses': {} }, ({ savedCourses }) => {
        const observer = new MutationObserver((mutations) => {
            for (const { addedNodes } of mutations) {
                for (const node of addedNodes) {
                    const courseElements = getCourseElements()
                    const courseElementsAdded = courseElements.length > handledCourseElements.size
                    // If a courseElement was added, update visibility of those that weren't already processed 
                    if (courseElementsAdded) {
                        const generatedCourses = generateCourseList(savedCourses)
                        courseElements
                            .filter(courseElement => !handledCourseElements.has(courseElement))
                            .forEach(courseElement => {
                                const courseName = getCourseElementTextContent(courseElement)
                                const courseShouldBeVisible = generatedCourses[courseName];
                                setCourseElementVisibility(courseElement, courseShouldBeVisible);
                                handledCourseElements.add(courseElement)
                            })
                    }

                    // The page header gets updated AFTER the courseList is updated - so once it's in the page, we know the courseElements are too
                    if (document.querySelector('header#page-header.row')) {
                        observer.disconnect()
                        chrome.storage.sync.set({ 'savedCourses': generateCourseList(savedCourses) });
                        return
                    }
                }
            }
        });
        observer.observe(document, { childList: true, subtree: true });

        // In case the content script has been injected when some of the DOM has already loaded
        onMutation([{ addedNodes: [document.documentElement] }]);
    });
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

記柔刀 2025-02-01 01:31:16

阅读存储速度很慢且异步,因此您需要在开始时进行:

chrome.storage.sync.get('courses', ({ courses }) => {
  chrome.storage.sync.set({ 'courses': generateCourseList(courses) });
  const observer = new MutationObserver(onMutation);
  observer.observe(document, { childList: true, subtree: true });
  onMutation([{addedNodes: [document.documentElement]}]);
  function onMutation(mutations) {
    for (const { addedNodes } of mutations) {
      for (const node of addedNodes) {
        if (node.tagName && node.querySelector(potentially_hidden_element_selector)) {
          observer.disconnect();
          processNode(node, courses);
        }
      }
    }
  }
});

function processNode(node, courses) {
  const courseElements = Array.from(
    node.closest('ul').querySelectorAll('a[data-parent-key="mycourses"]'));
  courseElements.forEach(courseElement => {
    const courseName = getCourseName(courseElement);
    const isVisible = courses[courseName];
    updateCourseElementInSidebar(courseElement, isVisible);
  });
}

Reading storage is slow and asynchronous, so you need to do it at the beginning:

chrome.storage.sync.get('courses', ({ courses }) => {
  chrome.storage.sync.set({ 'courses': generateCourseList(courses) });
  const observer = new MutationObserver(onMutation);
  observer.observe(document, { childList: true, subtree: true });
  onMutation([{addedNodes: [document.documentElement]}]);
  function onMutation(mutations) {
    for (const { addedNodes } of mutations) {
      for (const node of addedNodes) {
        if (node.tagName && node.querySelector(potentially_hidden_element_selector)) {
          observer.disconnect();
          processNode(node, courses);
        }
      }
    }
  }
});

function processNode(node, courses) {
  const courseElements = Array.from(
    node.closest('ul').querySelectorAll('a[data-parent-key="mycourses"]'));
  courseElements.forEach(courseElement => {
    const courseName = getCourseName(courseElement);
    const isVisible = courses[courseName];
    updateCourseElementInSidebar(courseElement, isVisible);
  });
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文