Stop page load at specified time - javascript

I am trying to debug an element that loads about for the first .5 - 1 second on page load. It completely disappears after that; I am wondering if there is any way at all I can stop the page while it's loading at a certain time, either with a script or emulator so I can track down this element with developer tools.

You can call window.stop() to stop window loading, if that is what you are looking for.
It sounds like it would be easier to pause the script execution with the browsers javascript debugger though, by setting some breakpoints instead of manually inserting a stop statement.

Second attempt, now that I understand your question a little better.
You can use a MutationObserver to output changes to the DOM. Something as simple as
var observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
console.log(mutation);
});
})
observer.observe(document.body, {
attributes: true,
childList: true,
characterData: true,
subtree: true
});
var hiders = document.querySelectorAll('.hider'),
hiders = Array.prototype.slice.call(hiders);
hiders.forEach(function(el) {
el.parentNode.removeChild(el);
});
<div class="hider"></div>
<div class="hider"></div>
in the window's load handler will work, and will output a MutationRecord on each change. I'd bet one of those will be your element (specifically, the removedNodes of the record.) The browser console for this snippet will have what you'll see if you use this.

Related

Mutation Observer fails to detect original element, does detect its duplicate

I have written a Chrome extension that loads a script with run_at set to document_start.
My script uses Mutation Observer in order to invoke some code upon the addition of an element with id X to a certain webpage.
That element is created when the page first loads, and then recreated when part of the page reloads following some button click.
My mutation observer fails to detect that element in either case.
However, if I duplicate the element (Developer Tools > Elements > element > context menu > Duplicate element), then my mutation observer does detect the duplicate.
I ran a test: I changed the id of some random element to X. It was not detected. That has made me suspect the original element is first created and then has its id assigned or updated. I am not sure how I could prove of refute this theory (the process of generating the webpage code looks very complex, with layers and layers of scripts involved, and I have very little experience).
Does this theory make sense? Can I observe an id change? If the theory does not make sense or if it is impossible to observe an id change, what alternatives do I have?
Thank you!
Edit:
My code looks like this:
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
for (let i = 0; i < mutation.addedNodes.length; i++) {
if (mutation.addedNodes[i].id == "X") {
alert("X detected!");
};
};
});
});
observer.observe(document, {
attributes: true,
characterData: true,
childList: true,
subtree: true
});

Jquery text function returns tags and js code

I am getting a text from elements that pop up on the page on change dome event (mutation). For every element that shows up I am collecting text from it. For the most websites, this approach works well but for some websites .text() returns tags and js scripts (it happens in gmail, outlook...)
var observer = new MutationObserver((mutations) => {
mutations.forEach(async (mutation) => {
console.log($(mutation.target).text())
})
})
observer.observe(document, { childList: true, subtree: true })
It looks like it returns almost everything inside of the element. I have tried to return only mutations from current element that is mutated:
$(mutation.target).clone().children().remove().end().text()
This does not capture every text mutation on the page, unfortunately. Other things that I have tried are regex clean up (unreliable, cuts of some things that I need) And some js only code with no success. I wonder if there is another way to get a clean text from the dome element?

Browser Extension: How to execute a function when an element is loaded

I am working on a browser extension.
It has two parts:
popup - which contains checkboxes
content script - which contains the code to alter the CSS property
I am saving the states of checkboxes so that the next time I open the popup again the same checkboxes are marked as checked.
When I use the checkboxes they change the DOM as intended, however when I try to alter the DOM after the page is loaded, changes are not reflected. This is probably because the element on which I want to perform the operation is loaded slow and thus required operations fail.
I tried to use onload and ready but nothing worked
$('.question-list-table').on('load', function() {
browser.storage.local.get(["options"], modifyThenApplyChanges)
});
I also tried, but nothing changed.
$('body').on('load','.question-list-table', function() {
browser.storage.local.get(["options"], modifyThenApplyChanges)
});
Also, there is no visible error with the popup or content script as I test in both Google Chrome and Mozilla Firefox.
Update:
As suspected earlier, the target element is loaded slowly so I used setTimeout for 5 seconds and the script is working as intended.
Loading time is variable and I want to show my changes as early as possible everything in a consistent manner.
After going through MutationObserver as suggested by #charlietfl in the comment section, this is what I coded and works for me
// Mutation Observer
const observer = new MutationObserver(function (mutations) {
mutations.forEach(function(mutation) {
if(mutation.addedNodes.length) {
//do stuff
}
});
});
el = document.getElementsById('elementId');
if(el) {
observer.observe(el, {
childList: true // specify the kind of change you are looking for
});
}

Image replacing Chrome extension, incomplete behaviour

I am new to web dev, so I'm sorry if this is a silly question. I followed a very simple tutorial and made a chrome extension that replaces images on screen with that of random ones I picked from my computer (mostly crappy meme valentines cards). However I have noticed some peculiar behaviour.
1) it doesn't replace all the images. for example in the google images page, it replaces the first 5 or 6 lines perfectly, and after that replaces none
2) similarly, on netflix it also does 1), but on top of that, if I go left or right on one of the sliders that it worked on, the images go away and it goes back to the default images.
How can I fix this so it 1) replaces all images and 2) keeps those changes.
I have attached a gif demonstrating this issue below
https://i.imgur.com/4hZuzsX.gif
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).

Apply code to dynamically loaded image

I have a page where an image with a wrapping div gets loaded via ajax. I need to apply some code when that image is loaded. I cannot alter the ajax call so I cannot simply use on('success') on the ajax call.
After some time the image with the div gets reloaded the same way (it is a kind of rotation).
The structure is like this:
<div id="spot">
<div id="xyz">
<a href="http://someurl.com">
<img src="http://someimage.url" />
</a>
</div>
</div>
The "spot" div is fixed and everything inside of it gets loaded dynamically.
When I put in
$('#xyz img').on('load', myCallback);
It doesn't work, I think because when the image gets reloaded, the listener is also removed.
I tried with the delegated listener:
$('#spot').on('load', '#xyz img', myCallback);
but didn't work either, it doesn't get executed (I've put console.log inside to verify what happens).
When I type in console $('#spot #xyz > img') the console shows the correct object.
How can I run my myCallback function right after the dynamic content with the image is ready for some DOM scripting action?
If you can't get access to the AJAX request callback then your options are limited to a setInterval() timer which checks to see if the elements have been added, a MutationObserver or a load event on an img element - but be aware this will only fire after the src of the image has loaded so there will be a delay.
Here's how you can use the MutationObserver, but be aware this will only work for newer browsers:
var targetElement = document.querySelector('#container');
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
// perform the required actions here...
console.log(mutation.type);
});
});
observer.observe(targetElement, { attributes: true, childList: true, characterData: true });
Not the correct way but you can try this
var handle = setInterval(showImage, 500);
function showImage(e) {
var img = $('#spot').find("img");
if(img != undefined) {
if($(img).is(":visible")) {
clearInterval(handle);
handle = 0;
// your logic
}
}
}

Categories