0


I created a quick, simple script for a chrome extension to add rel="noopener noreferrer" to all links with target="_blank" on a website.
Now the thing is (For example on Tumblr, if you scroll through your feed) if content gets loaded while browsing on the website, the script obviously wont add noopener to the link.
Now I thought of using MutationObserver but if I change the links with my script, MutationObserver would obviously recognize the change and I will get a infinte loop.

function gatherlinks() {
    var input = document.querySelectorAll('a[target="_blank"]');

    input.forEach(function addrelnoopener(link) {
        if (link.rel != 'noopener' || addrelnoopener.rel != 'noreferrer') {
            link.rel = 'noopener noreferrer';
        }
    });
}

Anyone has thoughts on how I could detect new links?

obeN
  • 416
  • 1
  • 5
  • 16
  • Infinite scroll effect is driven by AJAX requests, so perhaps you could listen for those and re-run `gatherLinks()`, here is an example of [Detecting any XMLHttpRequests](https://gist.github.com/alecperkins/3889507). Other alternative is poling - "eeew" – jaredrethman Mar 02 '19 at 11:29

1 Answers1

1

rel can have several space-delimited values that you wouldn't want to overwrite (see the list).

Use relList DOM interface for the rel attribute to add only the missing values so when the MutationObserver triggers it'll just skip these links and won't go into an infinite loop:

// process the current document
onMutation([{
  addedNodes: [document.body],
}]);

// process the future modifications
const mo = new MutationObserver(onMutation);
mo.observe(document, {subtree: true, childList: true});

function onMutation(mutations) {
  var links = [];
  // ES5 loops with vars are still much faster in 2019,
  // which is important for subtree:true, more info: https://stackoverflow.com/a/39332340
  for (var i = 0; i < mutations.length; i++) {
    var addedNodes = mutations[i].addedNodes; 
    for (var j = 0; j < addedNodes.length; j++) {
      var node = addedNodes[j];
      if (node.tagName === 'A' && node.target === '_blank') {
        links.push(node);
      } else if (node.firstElementChild && node.querySelector('a[target*="_blank"]')) {
        links.push(...node.querySelectorAll('a[target*="_blank"]'));
      }
    }
  }

  for (var k = 0; k < links.length; k++) {
    var link = links[k];
    if (!link.relList.contains('noopener')) {
      link.relList.add('noopener');
    }
    if (!link.relList.contains('noreferrer')) {
      link.relList.add('noreferrer');
    }
  }
}

Or you can attach a global mousedown handler on window and modify the clicked link:

window.addEventListener('mousedown', e => {
  const link = e.target.closest('a');
  if (link) {
    const {relList} = link;
    if (!relList.contains('noopener')) {
      relList.add('noopener');
    }
    if (!relList.contains('noreferrer')) {
      relList.add('noreferrer');
    }
  }
}, true);
wOxxOm
  • 65,848
  • 11
  • 132
  • 136