I know there is no such event, but is there something similar I can use?
I have ASP.NET usercontrols which are loaded dynamically into a container, which gives a very smooth effect.
The downside of doing this is that I want to run some client-side javascript when a control has finished loading, and I don't know how.
It took me a while to figure out why my jQuery scripts weren't running, then I realised they only ran on $(document).ready() and that only occurs when the entire page loads! (Duh!)
Can anyone think of a way of doing this?
Thanks :)
Getting more complicated now!
I'm trying to use a DOM Mutation Observer to watch the container so I can run a script when it changes.
But I'm getting an error: An attempt was made to reference a Node in a context where it does not exist.
The DOM element does exist, but I'm not sure I understand the error correctly.
Here is my observer code:
$(document).ready(function () {
// select the target node
var target = $('#contentPanel');
if (typeof (target) != 'undefined') {
// create an observer instance
var observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
console.log(mutation.type);
if (mutation.addedNodes) {
alert('New nodes');
}
//rememberMe();
})
});
// configuration of the observer:
var config = { attributes: false, childList: true, characterData: true }
// pass in the target node, as well as the observer options
observer.observe(target, config); // <== error occurs here
}
});
The final observer code:
$(document).ready(function () {
// select the target node
var target = $('#contentPanel');
if (typeof (target) != 'undefined') {
// create an observer instance
var observer = new MutationObserver(function (mutations) {
var doRememberMe = false;
mutations.forEach(function (mutation) {
if (mutation.addedNodes) {
doRememberMe = true;
}
})
if (doRememberMe) rememberMe();
});
// configuration of the observer:
var config = { attributes: false, childList: true, characterData: true }
// pass in the target node, as well as the observer options
//observer.observe(target, config);
observer.observe(document.body, config); // <== apply observer to whole document! (could be done nicer)
}
});
Got it, in that case try this at the end of your container (you can define CallLoadedEvent in a separate JS file):
<asp:Panel id="Container" runat="server">
<!-- controls dynamically load here -->
<script>
CallLoadedEvent();
</script>
</asp:Panel>
I was eventually able to get it working using the DOM Mutation code added above.
However the only way it would work was if I applied the listener to the whole document.
For some reason I wasn't able to apply it just to the element that was being modified; perhaps for some reason it was going out of scope, even though I was able to test it existed first! Oh well.
Related
I am attempting to check every mutation on a page using MutationObserver.
Is there a simple way to fix the following so I don't have to include it in the body (i.e. include it in the <head>) as an inline script?
The issue comes with the fact that I need the bodyList variable to be able to start monitoring items added to the page.
Obviously I can't use the onLoad event or anything as by then the mutations have all occurred (unless there is a way of accessing them after the fact I am unaware of?).
Or is there a way to attach the mutation observer to the document itself rather than an element?
I am sure the answer is simple but I can't find any documentation that covers this.
<body>
<script>
//I want to move this inline script into the <head>
var bodyList = document.querySelector("body"),
observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
console.log("MUTATION", mutation)
});
});
observer.observe(bodyList, {childList: true, subtree: true});
</script>
....all the HTML elements that I want to monitor
You can attach an observer to the HTML document itself, wait for the <body> to appear, then attach an observer to the body:
// In the <head>:
new MutationObserver((_, observer) => {
const { body } = document;
if (!body) return;
// Remove this observer, since it's not needed anymore; body exists
observer.disconnect();
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
console.log("MUTATION", mutation)
});
})
.observe(body, { childList: true, subtree: true });
})
.observe(document.documentElement, { childList: true });
I want to add a new class to body if an id has a specific style attribute using jQuery.
Why is this code not working?
if ($('#myID').css('display') == 'none'){
$('body').addClass('loaded');
}
Thank you in advance!
I found a solution. It works when I constantly check and not only once:
function check_loader(){
if ($('#ple-loader-wraps99').css('display') == 'none'){
$('body').addClass('loaded');
};};
window.setInterval(check_loader,1000);
You can listen for DOM changes on a specific DOM node using the MutationObserver API, then trigger an action:
const targetNode = document.getElementById('my-id');
// Specify which mutations to observe (options)
const config = { attributes: true };
// Simulate the preloader action (webpage load)
setTimeout(function() {
document.querySelector('#my-id').style ='display: none;'
}, 1000)
// Callback function to execute when mutations are observed
const callback = function (mutationsList, observer) {
console.log(`Mutation type: ${mutationsList[0].type}`)
console.log(`ID ${mutationsList[0].target.id}`);
// Make the necessary changes to your target elements here
document.querySelector('body').classList.add('loaded');
};
// 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);
.loaded {
background-color: crimson;
}
<div id="my-id">Loading..</div>
Note: All code is Vanilla JS (no jQuery).
Example created using code from the official documentation
I have the following script that runs on page load. It's meant to observe every td element on the page and check if it changes.
I was just logging the mutation to console to see my next steps, as I want to see what values change in each td element (the div refreshes every 15 seconds that houses the table).
Here's what I've got so far:
jQuery(document).ready(function( $ ) {
/** Change URL */
setInterval(function() {
$.ajax({
url: 'http://localhost:8888/profitmanager/wp-content/plugins/football-stats/update.php'
}).done(function(){
$('.fbs_results').load(
location.href+" .fbs_results>*", function(){
var hidden = [];
$.each(JSON.parse(localStorage.getItem("table_state")), function(index, value) {
if(value == 'hidden'){
$('tr[data-index="'+index+'"]').hide();
}
});
// console.log(hidden);
$('tr').each(function(index){
});
}
);
});
}, 15000);
const config = {
characterData: true,
characterDataOldValue: true,
childList: true,
subtree: true
};
function tdChanges(mutations) {
mutations.forEach((mutation) => {
console.log(mutation);
// if (mutation.addedNodes.value) {
// console.log();
// }
});
}
const tds = document.querySelectorAll('td');
Array.from(tds).forEach(function(td) {
const observer = new MutationObserver(tdChanges);
observer.observe(td, config);
});
});
But it doesn't work, nothing logs. Weird.
Can anyone fix my code?
TIA
querySelector() returns only a single Element. To look at all of them you need to use querySelectorAll() then loop over the result:
const tds = document.querySelectorAll('td');
Array.from(tds).forEach(function(td) {
const observer = new MutationObserver(tdChanges);
observer.observe(td, config);
});
Here's a working example in a jsFiddle, as the SO snippet editor is sandboxed and has issues running MutationObservers.
One thing to note here is that MutationObservers are not fast, and if you have a lot of td elements in your page you may see a performance hit. You may be better served by placing a single MutationObserver on the parent table and letting the event bubble up, like this.
My problem basically is that i want to click on a <button> that appears inside a <div> that is available only for 3 seconds...
The <div> has display:none, so i can make it appear whenever i want, but the <button> is generated by an other js file that i don't have access to. I can not change the original js file (to give me more time, for example) because i'm only interacting with the page using a userscript! (not my own page)
I tried so far locating inside the js code the id or the class of the button generated, but the js is minified... so no luck...
I tried also using the temporary class that is generated during those 3 seconds for that button and click on it... by doing a screenshot typing the class manually and then running a little code in the console during those 3 seconds to click on it... and no luck...
By now i think the problem is my approach to the situation, so i didn't write any code here...
Please give me your thoughts about this...
You can add MutationObserver if you want to react to changes in DOM (in your case to parent element).
Without any code it's hard to provide better answer.
jsFiddle
(function () {
"use strict";
var target = document.getElementById("hiddenElement"),
observer = new MutationObserver(function(mutations) {
mutations.forEach(function(value, index, array) {
console.log(value.type);
console.dir(value.addedNodes); // return node list
console.dir(value.removedNodes);
// put here your logic
});
});
observer.observe(target, {
childList: true,
subtree: true,
characterData: true,
characterDataOldValue: true
});
}());
(function () {
"use strict";
var newEle = document.createElement("input"),
hiddenElement = document.getElementById("hiddenElement");
newEle.type = "button";
newEle.value = "click";
newEle.addEventListener("click", function(e) {
alert("Generated only for 3 second");
});
hiddenElement.appendChild(newEle);
hiddenElement.style.display = "block";
setTimeout(function() {
hiddenElement.style.display = "none";
hiddenElement.removeChild(newEle);
}, 3000);
}());
Throw your button detection code inside a setInterval that fires often enough that it will fire while the button is present:
// note: this is just pseudocode
var interval = setInterval(function() {
// look for button
if(buttonFound) {
clickButton();
clearInterval(interval);
}
}, 300);
This question already has answers here:
How to listen for changes to the title element?
(6 answers)
Closed 3 years ago.
Is there any way to detect a change to document.title / head > title via Javascript? I want to detect this via a Google Chrome extension content script, so I can't really wire up code in the target page's JS where the actual title change is performed.
I've found WebKitMutationObserver which theoretically should be able to detect a change to head > title, but it doesn't work for all cases:
// set up an observer for the title element
var target = document.querySelector('title');
var observer = new WebKitMutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
console.log(mutation);
});
});
var config = { attributes: true, childList: true, characterData: true };
observer.observe(target, config);
// this jQuery statement fires the observer as expected ...
$('head > title').text('foo');
// ... but this doesn't:
document.querySelector('title').innerText = 'cheezburger';
// ... and neither does this:
document.title = 'lorem ipsum';
Any ideas?
I have found a fully working solution which is only a small modification to the example I posted in the original post.
// set up an observer for the title element
var target = document.querySelector('head > title');
var observer = new window.WebKitMutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
console.log('new title:', mutation.target.textContent);
});
});
observer.observe(target, { subtree: true, characterData: true, childList: true });
// all three of these methods correctly fire the mutation observer
setTimeout(function() { document.title = 'foo'; }, 1000); // the usual method
setTimeout(function() { document.querySelector('head > title').innerText = 'bar'; }, 2000); // DOM method
setTimeout(function() { $('head > title').text('cheezburger'); }, 3000); // jQuery-only method
The addition of subtree: true was all that was needed to get this working right.
The wrapping of the three title-changing methods in setTimeout calls at the end is just for demonstration purposes; without this the title value changes so quickly that the WebKitMutationObserver doesn't report each change individually, since MutationObserver is designed to accumulate changes over a short period before executing the observer callback.
If one does not need to detect title changes made via the last jQuery-only method, the childList: true property can be omitted from the observer.observe line; only characterData: true is needed to detect the first two title-changing methods.
You have both JQuery and Javascript in your code example. Not sure if your only restricted to JavaScript, but here's how you can do it with jQuery
If you want to trigger the change, take a look at: http://api.jquery.com/trigger/
jQuery
$(document).ready(function () {
$("title", "head").change(function () {
console.log("Title has changed");
});
//Trigger Change
$("title","head").text("New Title").trigger("change");
});