3

I am trying to create a Chrome Extension to manipulate the HTML on a page. The extension should remove or hide some elements from a page when activated.

I created a simple manifest.json file:

{
  "manifest_version": 2,

  "name": "hide-msgs-ext",
  "version": "0.0.1",
  "description": "Hide msgs extension",

  "content_scripts": [{
    "js": ["content.js"],
    "matches": ["https://website.example.com/*"],
    "run_at":  "document_idle"
  }]

}

and a script content.js, which should remove/hide all my target elements:

var elements = document.querySelectorAll('div.card');
for (var i = 0, l = elements.length; i < l; i++) {
  subElement = elements[i].querySelector(':scope div.card-body');
  if (subElement.firstChild.tagName != 'A') {
    elements[i].style.display = "none";
  }
}

If I execute the above script in the chrome console I am able to hide all the elements I want and no errors are triggered. However I could not figure it out how to make it work in the extension.

Could someone point me in the right direction? Thanks you in advance!

R. Dureya
  • 33
  • 1
  • 7
  • does it work when you run it in the chrome console? Any errors? – Jason Goemaat Sep 11 '20 at 16:29
  • Is it running before the page loads? document.onload = .... – Moshe Sommers Sep 11 '20 at 17:07
  • @JasonGoemaat, it works in the console just fine, no errors. – R. Dureya Sep 12 '20 at 09:29
  • @MosheSommers it is possible that this is the issue. I tried to add an event listener `document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);` like suggested [here](https://stackoverflow.com/questions/28186349/chrome-extension-set-to-run-at-document-start-is-running-too-fast) but it did not work... any suggestions? thanks! – R. Dureya Sep 12 '20 at 11:10
  • I also followed up the `document.onload` idea, @MosheSommers. Tried with `"run_at": "document_idle"` and `"run_at": "document_end"` as indicated [here](https://developer.chrome.com/extensions/content_scripts#run_time), but also did not work :( – R. Dureya Sep 12 '20 at 11:21
  • 1
    If it is the case that the content is created by javascript, you might need to set a timer to run your script later. Hooking into whatever framework the page is running is probably too difficult, but you could wrap your code in `function myFunction() {}` and do 'setTimeout(myFunction, 5000)` to check that out. If that fixes it you could look into 'MutationObserver' and listen for dom changes so you can remove them right away. – Jason Goemaat Sep 12 '20 at 18:36
  • 1
    To verify your script is loading and the function is working from the content script, wrap your code in a function and call it at the end of your script, then do a `console.log('Script loaded!')` or something. Then when you see the cards you think should be removed, test the code by going to the chrome console and selecting your script's context from the drop-down (initially says 'top') and try to run your function in the console. If that works, try the timeout above... – Jason Goemaat Sep 12 '20 at 18:39
  • @JasonGoemaat, the `setTimeout` solved the issue. Thanks a lot for your help! I will look into the 'MutationObserver' as you suggested. If you want you can use this information to answer the question so that I can give you the proper credit. – R. Dureya Sep 12 '20 at 20:24

1 Answers1

1

It sounds like the content might be created by javascript after the page loads so you need to wait for it to appear. You can wrap your code in a function and execute it after a timeout:

function hideCards() {
  var elements = document.querySelectorAll('div.card');
  for (var i = 0, l = elements.length; i < l; i++) {
    subElement = elements[i].querySelector(':scope div.card-body');
    if (subElement.firstChild.tagName != 'A') {
      elements[i].style.display = "none";
    }
  }
}

// execute after a second to give the page time to create the
// elements you want to remove
setTimeout(hideCards, 1000);

If you want it to work throughout the life of the page and be very quick, you could try using MutationObserver. This lets you listen for DOM changes. Here's a sample of how to use that. The 'Add Item' button adds a new item, every other one has the 'remove-me' class. The MutationObserver executes a function that automatically removes any LI nodes added with that class so they don't appear:

const observer = new MutationObserver(function(mutations) {
  let added = [];
  for (let i = 0; i < mutations.length; i++) {
    let m = mutations[i];
    for (let j = 0; j < m.addedNodes.length; j++) {
      let n = m.addedNodes[j];
      if (n.tagName === 'LI' && n.classList.contains('remove-me')) {
        added.push(n);
      }
    }
  }
  added.forEach(element => element.remove())
});

const config = { subtree: true, childList: true };
observer.observe(document.body, config);
 
let index = 0;
document.getElementById('btnAddItem')
.addEventListener('click', (event) => {
  let li = document.createElement('li');
  if (index & 1 == 1)  li.classList.add('remove-me');
  li.innerText = `Item ${index++}`;
  document.getElementById('myList').appendChild(li);
});
<ol id="myList">
  <li>First Item</li>
</ol>

<button id="btnAddItem">Add Item</button>
Jason Goemaat
  • 28,692
  • 15
  • 86
  • 113