隐藏扩展名为 chrome 的 DOM 元素而不引起闪烁
Hiding DOM elements with a chrome extension without causing a flicker
前言:
我知道那里有一个 duplicate question。我再次发布它是因为它没有答案(而且是 4 年前的)。
我想要的大致描述:
我希望能够在 DOM 加载到视图之前隐藏 DOM 元素(添加 Element.style.display = "none"
)。
我试过的:
其他帖子指出在 document
元素上使用 MutationObserver 并运行将其
为了确保我们能够在加载 DOM 之前隐藏元素,我们要 运行 包含 MutationObserver 的脚本作为 content_script
和 "run_at":"document_start"
.
我做了所有这些,但我仍然看到闪烁(元素在我加载页面时出现,然后很快消失)。
我想做什么:
有一个 ul
,其中包含一些 li
以及我将 content_script.js
注入的页面上的一些文本。我用 <text, checkbox>
对填充我的 popup.html
。如果选中该复选框,则包含所述文本的 li
可见,否则隐藏。我希望它在刷新之间持续存在,因此使用 storage
.
一切正常 - 但每当我刷新页面时都会出现闪烁。元素在那里,然后它们就消失了。我不想让他们首先出现!
我的代码:
当我检测到我可能删除的 DOM 元素已加载时,我会生成一个对象,指示我是否应该隐藏或保持该特定 DOM 元素可见。
然后我相应地将其 Element.style.display
设置为 none
或 block
。
/**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 元素 - 所以我无法在 DOM 加载之前调用 chrome.storage.set
方法想想。
当我刷新页面时,课程列表最终填充 DOM。
然后我根据这些课程元素的 innerText 属性填充存储的 courses
对象。我根据以下两个因素之一将这些元素的可见性设置为 true
或 false
:如果此课程已在 courses
对象中定义,则保持其可见性状态,否则't,设置为true
(默认可见)。
我无法确定某些 DOM 元素 visible/hidden 如果我没有参考它们。因此,如果我尝试在那些特定的 DOM 元素加载之前调用 generateCourseList
,我最终会尝试检索所有课程元素 (document.querySelectorAll('a[data-parent-key="mycourses"]')
) 并且什么也得不到返回。由于这个 chrome.storage.sync.set({ "courses": generateCourseList(courses) }...
.
,我最终将 chrome.storage
中的 courses
设置为空
编辑 2:
这是我的全部代码。我会尽快 chrome.storage.sync.get
,并且尽量不依赖 chrome.storage.sync.set
.
的结果
我尝试尽快删除这些元素,但我很难做到。这是因为我很难知道我想要访问的内容(课程元素)何时已完全加载。以前,我正在检测一个课程元素何时可见,当它可见时,我假设所有元素都是可见的。这是一个错误。我能够在它弹出的那一刻访问一个课程元素,但有时实际上只加载了 6 个课程元素中的 4 个。我不能硬编码这个数字,因为它因人而异。我不能一个一个地解决它们,因为那样我就不知道什么时候断开 MutationObserver 的连接。我使用调试器并试图在所有 6 个课程元素加载后立即找到加载的元素,即 header#page-header.row
元素。我仍然会出现闪烁,虽然没有以前那么明显了。
我可以做些什么来让它不那么引人注目?
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 中的课程元素的可见性。现在基本上没有闪烁(有轻微的闪烁,但没有我的扩展,它的闪烁量是一样的)。
这是 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] }]);
});
}
读取存储速度慢且不同步,需要一开始就做:
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);
});
}
前言:
我知道那里有一个 duplicate question。我再次发布它是因为它没有答案(而且是 4 年前的)。
我想要的大致描述:
我希望能够在 DOM 加载到视图之前隐藏 DOM 元素(添加 Element.style.display = "none"
)。
我试过的:
其他帖子指出在 document
元素上使用 MutationObserver 并运行将其
为了确保我们能够在加载 DOM 之前隐藏元素,我们要 运行 包含 MutationObserver 的脚本作为 content_script
和 "run_at":"document_start"
.
我做了所有这些,但我仍然看到闪烁(元素在我加载页面时出现,然后很快消失)。
我想做什么:
有一个 ul
,其中包含一些 li
以及我将 content_script.js
注入的页面上的一些文本。我用 <text, checkbox>
对填充我的 popup.html
。如果选中该复选框,则包含所述文本的 li
可见,否则隐藏。我希望它在刷新之间持续存在,因此使用 storage
.
一切正常 - 但每当我刷新页面时都会出现闪烁。元素在那里,然后它们就消失了。我不想让他们首先出现!
我的代码:
当我检测到我可能删除的 DOM 元素已加载时,我会生成一个对象,指示我是否应该隐藏或保持该特定 DOM 元素可见。
然后我相应地将其 Element.style.display
设置为 none
或 block
。
/**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 元素 - 所以我无法在 DOM 加载之前调用 chrome.storage.set
方法想想。
当我刷新页面时,课程列表最终填充 DOM。
然后我根据这些课程元素的 innerText 属性填充存储的 courses
对象。我根据以下两个因素之一将这些元素的可见性设置为 true
或 false
:如果此课程已在 courses
对象中定义,则保持其可见性状态,否则't,设置为true
(默认可见)。
我无法确定某些 DOM 元素 visible/hidden 如果我没有参考它们。因此,如果我尝试在那些特定的 DOM 元素加载之前调用 generateCourseList
,我最终会尝试检索所有课程元素 (document.querySelectorAll('a[data-parent-key="mycourses"]')
) 并且什么也得不到返回。由于这个 chrome.storage.sync.set({ "courses": generateCourseList(courses) }...
.
chrome.storage
中的 courses
设置为空
编辑 2:
这是我的全部代码。我会尽快 chrome.storage.sync.get
,并且尽量不依赖 chrome.storage.sync.set
.
我尝试尽快删除这些元素,但我很难做到。这是因为我很难知道我想要访问的内容(课程元素)何时已完全加载。以前,我正在检测一个课程元素何时可见,当它可见时,我假设所有元素都是可见的。这是一个错误。我能够在它弹出的那一刻访问一个课程元素,但有时实际上只加载了 6 个课程元素中的 4 个。我不能硬编码这个数字,因为它因人而异。我不能一个一个地解决它们,因为那样我就不知道什么时候断开 MutationObserver 的连接。我使用调试器并试图在所有 6 个课程元素加载后立即找到加载的元素,即 header#page-header.row
元素。我仍然会出现闪烁,虽然没有以前那么明显了。
我可以做些什么来让它不那么引人注目?
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 中的课程元素的可见性。现在基本上没有闪烁(有轻微的闪烁,但没有我的扩展,它的闪烁量是一样的)。
这是 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] }]);
});
}
读取存储速度慢且不同步,需要一开始就做:
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);
});
}