在 chrome 扩展中使用内容脚本插入 DOM 元素

Inserting DOM elements using content script in chrome extension

我正在尝试为 twitter 做一个扩展,但我一直坚持在这个部分插入 div 块(有点像 btn)

Manifest.json

{
    "manifest_version": 3,
    "name": "Twitter Saver",
    "version": "0.0.1",
    "description": "Twitter Saver| Save tweets.",
    "permissions": ["tabs", "https://*.twitter.com/*"],
    "icons": {
        "128": "img/icon-app-128.png",
        "256": "img/icon-app-128@2x.png"
    },
    "content_scripts": [
        {
            "html": ["save-dropdown.html"],
            "js": ["js/jquery-3.6.0.min.js", "js/content.js"],
            "matches": ["https://*.twitter.com/*"]
        }
    ]
}  

Content.js

$(
        "<div>",{
            class: "css-1dbjc4n r-18u37iz r-1h0z5md",
            text: "HELLO WORLD"
        }
    ).appendTo(".css-1dbjc4n.r-1ta3fxp.r-18u37iz.r-1wtj0ep.r-1s2bzr4.r-1mdbhws");

我调查了一些事情,例如 MutationObserver 但是这里我得到了错误,因为参数不是节点

let react_root = $("#react-root");
let callback = function(changeList, observer) => {
         console.log(changeList);
}
const observer = new MutationObserver();
observer.observe(callback, {childList: true});

Twitter 在向下滚动添加新帖子时删除以前的帖子,并在滚动回到顶部时添加以前的帖子删除新帖子

对于Twitter,MutationObserver是必经之路,至于如何使用,我更喜欢this Mozilla guide

为了让 get 正常工作,请注意语法:这里在注册 MutationObserver 时,它表示观察整个主体和后代,并在发生变化时调用函数 appendCustomNode

// Step 1. Create an observer instance linked to the callback function
// const observer = new MutationObserver(callback);
// Step 2. Start observing the target node for configured mutations
// observer.observe(targetNode, config);

const observer = new MutationObserver(appendCustomNode); 
observer.observe(document.body, {subtree: true, childList: true});

下一步是定义这个函数appendCustomNode

在计划如何将节点附加到卡片时,有必要考虑到您可能会多次看到同一个节点,这需要制定仅附加一次自定义元素的策略。选择一些足够随机的值,然后用该值标记所有以前看到的节点,将有助于实现这一点。这是一个例子:

const customTag = `tag-${Date.now()}` // your choice of a valid & unique value

function appendCustomNode(){

  // find all timeline cards, their tag name is article
  const cards = document.getElementsByTagName('article');

  // check each card
  for (let n = 0; n < cards.length; n++) {

     const card = cards[n];

     // check that cards is new / has not been seen before
     if (!card.hasAttribute(customTag)) { 

       addButton(card);

       // mark the card as "processed" by adding the custom tag
       card.setAttribute(customTag, true);
     }
  }
}

最后实现将要添加的按钮附加到卡片的逻辑。这里的 card 属性表示一个时间线项目的 DOM 节点。

function addButton(card){

   const myButton = document.createElement('div');
   myButton.innerText = 'hello world';

   // select the div of action buttons
   // or wherever you want to insert your custom node 
   // SEE NOTE BELOW
   const target = card.querySelector('....?....')

   // append/prepend/insertAdjacentHTML the button here
   target.append(myButton);
}

出于以下几个原因,我将保留最后一部分:在决定如何在卡片中查找按钮行时,如果使用 类 作为 selector,如果类 改变,这是你无法控制的。在扩展 3rd 方网络应用程序时,这始终是一个挑战。我建议尝试找到一个更可靠的 selector(这就是为什么我之前使用标签名称文章来 select 卡片)。其次 buttons/card 结构周期性变化,所以这是另一个需要考虑的问题:你想在哪里插入这个自定义节点。最后,这种方法不需要 jQuery.