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.
Related
In one of our projects we load slides into our webpage with use of AJAX. After the slides are loaded I want jQuery to execute a plugin on all new injected elements automatically.
This is the code I found else where but it didn't do the trick. Also I've tried to replace .on function with the .bind function but then the whole site died and JavaScript crashes with an overflow.
function loaded(selector, callback) {
//trigger after page load.
jQuery(function () {
callback(jQuery(selector));
});
//trigger after page update eg ajax event or jquery insert.
jQuery("body").on('DOMNodeInserted', selector, function (e) {
callback(jQuery(this));
});
}
I got the problem reproduced in a JSFiddle.
A. Wolff,
Thanks your answer solves my problem. I have edited the loaded function to the following:
function loaded(selector, callback) {
//trigger after page load.
jQuery(function () {
callback(jQuery(selector));
});
var parentSelector = "* > " + selector;
//trigger after page update eg ajax event or jquery insert.
jQuery(document).on('DOMNodeInserted', parentSelector, function (e) {
callback(jQuery(this).find(selector));
});
}
Also I've forked a new working JSFiddle project for anyone who wants a full working example.
https://jsfiddle.net/9t8cahqv/
Thanks,
Jop
Event is fired on container DIV level so your selector isn't matching. You could use instead:
loaded(":has([title])", function(element) {
element.tooltip();
});
/**/
jQuery(document).on('DOMNodeInserted', selector, function(e) {
callback(jQuery(this).find('[title]'));
});
-jsFiddle-
Another way of doing this might be, watch for a DOM
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (!mutation.addedNodes) return
for (var i = 0; i < mutation.addedNodes.length; i++) {
// do things to your newly added nodes here
var node = mutation.addedNodes[i]
if ($(node).hasClass("some class")) {
$(node).remove()
}
//or with id
if ($(node).attr("id") == "someId") {
$(node).remove()
}
//or any other selector
}
})
})
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: false,
characterData: false
})
To Stop Observe, use
observer.disconnect();
I have jQuery replaceWith call, and I want to pop up an alert only when the replaceWith finishes loading.
To achieve this I have this very naive javascript implementation:
$(document).ready(function (){
$("#myDiv").click(function () {
$("#myDiv").replaceWith("<div>Hello World!!!</div>");
alert("done");
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="myDiv">Hello!</div>
The problem is that in this case the alert will pop-up independently of the time that the replaceWith takes. If it is fast, no problem, but if the replaceWith takes several seconds to load (which is the real case) then the pop-up appears way before, and I want to avoid that.
How can I achieve the behaviour I am looking for?
Try
$(document).ready(function() {
var body = $("body");
$("#myDiv").click(function(e) {
var html = $("<div>Hello World!!!</div>");
$("#myDiv").replaceWith(html)
.promise().done(function(elem) {
if (body.find(html).is("*")
&& !body.find(elem).is("*")) {
alert("done");
}
});
});
});
$(document).ready(function() {
var body = $("body");
$("#myDiv").click(function(e) {
var html = $("<img src=http://lorempixel.com/output/cats-q-c-1920-1920-2.jpg />");
// $("<div>Hello World!!!</div>");
$("#myDiv").replaceWith(html)
.promise().done(function(elem) {
if (body.find(html).is("*")
&& !body.find(elem).is("*")) {
alert("done");
}
});
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="myDiv">Hello!</div>
I would try getting the value of what you're replacing - then check if it exists after the replacement - then you can alert its complete.
fiddle:
http://jsfiddle.net/eavxkc3f/
jquery:
$(document).ready(function (){
$(".myDiv").click(function () {
var currentItem = $(".myDiv").html();
var replacer = "<div>Hello World!!!</div>";
$(".myDiv").replaceWith("<div>Hello World!!!</div>");
if($(".myDiv").html != currentItem){
alert("Done.");
}
});
});
Take a look at the DOM MutationObserver spec, I think it does what you want. Register it on a target node, and it will watch for changes beneath that target.
It's an updated version of the now deprecated Mutation events
A blog post with additional good info (and where I found this sample code)
Working example in this fiddle (code below)
$(document).ready(function (){
$("#myDiv").click(function () {
$("#myDiv").replaceWith("<div>Hello World!!!</div>");
});
});
// select the target node
var target = document.querySelector('#divParent');
// create an observer instance
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
//console.log(mutation.type);
alert('Done');
});
});
// configuration of the observer:
var config = { attributes: true, childList: true, characterData: true };
// pass in the target node, as well as the observer options
observer.observe(target, config);
// later, you can stop observing
//observer.disconnect();
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);
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.
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");
});