It will happen mainly for two reasons:
- Not all the
img
tags exist when you perform the replacement;
- The elements get rerendered when you perform some action on the page (like clicking or scrolling).
I tested in Google Image Search and yep - not all the img
tags are there from the start. So you'd have to replace them, too, when they appear.
Go to Google Image Search and test this script once. Then scroll down to the bottom and test it once again to see if it changed:
// this will tell you how many img tags exist in the page
console.log(document.getElementsByTagName('img').length);
It most certainly increased as you were scrolling down. Right?
Possible workaround - observing element mutations
In days of old there were events you could listen to for changes whenever elements were added or removed, but they've been deprecated for quite some time. Let's get this out of the way: don't use mutation events!
Nowadays if you want to monitor changes in the page I suggest you take a look at Mutation Observers.
Mutation Observers
A mutation happens when an element or its contents or attributes are changed. So in short, Mutation Observers provide you a way to monitor changes in an element.
The most relevant difference (at least for extension developers) between observing mutations and listening to events is that mutations capture changes that are done programmatically, which makes it so useful since more often than not we must alter a page whose source we did not author.
Could you listen to a given event like, say, scrolling? Yes. But since there most likely are event listeners going on concurrently with your own that will cause mutations in the page, it's hard to make sure that the element you want to change yourself will already exist by the time the event is triggered (the page's native code might still be doing its thing).
Example
Here's an example borrowing from the above MDN link, plus the desired behavior you described - replacing images, whenever they are added:
EDIT: I'm going to be more descriptive in the comments and leave links to where you should browse for additional information in the MDN docs in case you need them. Note that the example is targeting a specific element (#some-id
) but you could target document.body
, but not in Stack Overflow's fiddle sandbox, apparently.
// Select the node that will be observed for mutations
// I'm targeting a specific node, but it COULD BE document.body,
// which would observe the entire document for changes
const targetNode = document.getElementById('some-id');
// Options for the observer (which mutations to observe)
// See https://developer.mozilla.org/en-US/docs/Web/API/MutationObserverInit
const config = {
attributes: true, // watch for changes to attributes on the node(s)
childList: true, // watch for the addition or removal of child nodes
subtree: true // all monitoring rules apply to child elements as well
};
/*
PS: all settings are 'optional' but at least one among
childList, attributes or characterData must be true.
Again, don't forget to take a look on the docs!
*/
// Callback function to execute when mutations are observed
// See https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver/MutationObserver#The_callback_function
const callback = function(mutationsList, observer) {
// mutationsList is an array of MutationRecord objects,
// describing each change that occurred.
// See https://developer.mozilla.org/en-US/docs/Web/API/MutationRecord
// observer is the MutationObserver instance that was triggered
for (let mutation of mutationsList) {
if (mutation.type === 'childList') {
// This will be executed when a new image is added
console.log('A child node has been added or removed.');
// Iterate over the added nodes to check if there are any images
for (let node of mutation.addedNodes) {
// Replace the .src attribute if the element is an img
if (node.tagName === 'IMG') {
node.src = 'https://pm1.narvii.com/6334/121fb1f34767638906fac47cf818dc9c326bc936_128.jpg';
}
}
console.log('Garmanarnar will rule above all');
} else if (mutation.type === 'attributes') {
// This will be executed when .src is changed
console.log('The ' + mutation.attributeName + ' attribute was modified.');
}
}
};
// Create an observer instance linked to the callback function
const observer = new MutationObserver(callback);
// Start observing the target node for configured mutations
observer.observe(targetNode, config);
// Later, you can stop observing
// observer.disconnect();
<script>
function addImg() {
console.clear();
let img = document.createElement('img');
img.src = 'https://loremflickr.com/320/240';
document.getElementById('some-id').appendChild(img);
}
</script>
<button style="cursor: pointer;" onclick="addImg()">
<strong>Click me</strong>
</button>
<div id="some-id">
<img src="https://pm1.narvii.com/6334/121fb1f34767638906fac47cf818dc9c326bc936_128.jpg" alt="Garmanarnar" />
</div>
With each click, you should see only Garmanarnars (blue friendly alien doing thumbs up), not any random pic of cats or anything else. This means the new img
tag got its src
attribute replaced successfully.
In Javascript we use lots and lots of callback functions, so if you're not used to them, you might have a lot to digest, but it will be worth your while learning them.
Additional considerations (you may skip if you're satisfied)
I get that it's just a prank and it would be a 'passable' flaw, but if you want to target every single image in a page, looking for img
tags might not be always enough, since some elements will have images included through CSS background-image
property, or even svg
elements.
I made a Chrome extension myself a few years ago and maybe it's also worth mentioning that you must pay attention whether an image is part of an iframe
or not. They're not part of the current document - it's a page from elsewhere and it's sandboxed for security reasons. Take a look at this answer from another Stack Overflow question to see how to enable accessing iframes within a page (you still have to create rules for the target page, though).