Search code examples
javascriptgoogle-chrome-extension

remove elements by javascript without content shift


I created a chrome extension which is used to block advertisements elements from some websites. My content script is as below:

var selectors = [
            ...
        ];
for(var i in selectors){
    if(selectors.hasOwnProperty(i)){
          var elements = document.querySelectorAll(selectors[i]);
          console.log('elements: ', elements);
          for (var k in elements) {
              if (elements.hasOwnProperty(k)) {
                   elements[k].parentElement.removeChild(elements[k]);
              }
          }
     }
}

when I wrap the code in document.addEventListener("DOMContentLoaded", function() {}, it shift the content and when I don't wrap the code in DOMContentLoaded the elements list is empty. what should I do to remove the advertisement element without content shift?


Solution

  • Rather than waiting for the whole document to load, you could consider inserting a <style> tag immediately, and have rules inside that hide those selectors.

    const selectors = [ ... ];
    // run ASAP:
    document.body.appendChild(document.createElement('style'))
      .textContent = selectors.join(', ') + ' { display: none }';
    // Run after document loads:
    document.addEventListener("DOMContentLoaded", () => {
      // ...
    });
    

    This will result in a <style> tag with content like

    .ad1, .ad2, .ad3 { display: none }
    

    This way, the removal of the bad elements won't result in content shifting, for the most part, because they already have display: none.

    Another benefit of such a stylesheet is that it'll take effect immediately, without any additional JavaScript, whenever an element is inserted. So, for example, if a page inserts an ad somewhere every 30 seconds, the CSS rules in the <style> can prevent it from being seen without having to have additional JS to deal with the dynamically generated content.

    Arguably, if you just hide the ads with the stylesheet, that may be all you need to accomplish. You can additionally remove them from the DOM entirely if you want (perhaps lowering bandwidth usage), but the user probably won't notice a difference between hidden ads and removed ads.

    A MutationObserver attached on pageload is another option - instead of waiting for everything to finish first, you can remove blacklisted elements as soon as they get inserted into the DOM.