Reputation: 179
I'm trying to make an extension for twitter and I'm stuck at inserting the div block (Kinda like btn) in this section
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/[email protected]"
},
"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");
I've looked into few things like MutationObserver but here I got error as parameter is not a node
let react_root = $("#react-root");
let callback = function(changeList, observer) => {
console.log(changeList);
}
const observer = new MutationObserver();
observer.observe(callback, {childList: true});
Twitter removes the previous posts as scrolling down adding new ones and adds the previous ones removing new ones when scrolling back to top
Upvotes: 0
Views: 2673
Reputation: 1434
For Twitter, MutationObserver is the way to go, and for how to use it, I prefer this Mozilla guide.
To get is working correctly, note the syntax: here in registering the MutationObserver, it says watch the entire body and descendants, and call function appendCustomNode
when change occurs.
// 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});
Next step is to define this function appendCustomNode
.
In planning how to append nodes to the cards, it is necessary to take into consideration that you will see the same node possibly multiple times, which requires having a strategy to append the custom element only once. Picking some sufficiently random value, then tagging all previously seen nodes with that value, will help accomplish this. Here is an example:
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);
}
}
}
Lastly implement the logic to append the button you want to add to the card. Here card attribute represents the DOM node of one timeline item.
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);
}
I will leave this last part open for a few reasons: when deciding how to find the buttons row in the card, if using classes as a selector, the implementation breaks if the classes change, which is out of your control. This is always a challenge when extending 3rd party web apps. I suggest trying to find a more reliable selector (which is why earlier I used tag name article to select cards). Secondly the buttons/card structure changes periodically, so that is another point to consider: where do you want to insert this custom node. Lastly this approach does not require jQuery.
Upvotes: 1