I want to execute some code if this element exists <div id="element">Some text</div> using jquery.
my code until now:
setInterval(function(){check()}, 100);
function check() {
var count = document.getElementById("count");
if(document.getElementById("element") && count.value != 1) {
//some code
count.value = 1;
}
}
It works, but I think this is a very bad way to reach my target.
I want an easier solution, but I didn't find.
Your way is the most reliable, because it cannot fail.
However, you may wish to try listening for change events on the count element, and reset the value if your element exists. This will mean your verification code only runs when a change is made to the value.
do you want to do this initially?
hjow about you do this?
$(document).ready(function(){
if($(#element)) { do something };
});
EDIT:
after 10 seconds of search:
$("#someDiv").bind("DOMSubtreeModified", function() {
alert("tree changed");
});
You can listen DOM events (when an element is inserted or modified) and check your condition only at this time, not every time interval.
If you need some information about DOM events you can take a look at : http://en.wikipedia.org/wiki/DOM_events#HTML_events (mutation events)
One solution I can think of is about using MutationObserver with a fallback mechanism like
jQuery(function() {
if (window.MutationObserver) {
var target = document.querySelector('#myparent');
// create an observer instance
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
[].forEach.call(mutation.addedNodes, function(el, i) {
if (el.id == 'element') {
check();
//if you don't want to listen any more remove the listener
//observer.disconnect();
}
})
});
});
// configuration of the observer:
var config = {
childList: true
};
// pass in the target node, as well as the observer options
observer.observe(target, config);
} else {
setInterval(function() {
if (document.getElementById("element")) {
check();
}
}, 100);
}
function check() {
var count = document.getElementById("count");
if (count.value != 1) {
//some code
count.value = 1;
}
}
});
$('button').click(function() {
$('#myparent').append('<div id="element">Some text</div>');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<button>Add</button>
<input id="count" />
<div id="myparent"></div>
Note: The solution assumes you have a static parent element for the dynamic element(like the myparent element in the above example)
Related
I am having some trouble detecting attribute changes of an html element with js and MutationObserver. This is the code:
document.addEventListener("DOMContentLoaded", function() {
const checkForLoading = setInterval(function () {
let loading = document.getElementById("sequence");
if (loading) {
console.log("loading detected");
const loadingObserver = new MutationObserver(function (mutations) {
console.log("mutation detected");
if (loading.getAttribute('data-dash-is-loading') === 'true') {
console.log("loading");
// loading.style.visibility = 'hidden';
} else {
console.log("not loading");
// loading.style.visibility = 'visible';
}
});
const observerOptions = {
attributes: true,
}
loadingObserver.observe(loading, observerOptions);
clearInterval(checkForLoading);
}
}, 100);
});
Because the element is not available immediately I have the checkForLoading loop set up. The attribute 'data-dash-is-loading' is only set when an element is loading and otherwise not available. This code only works if the loop keeps on running after the sequence element is detected and clearInterval(checkForLoading) is not called. However I would like to avoid running this loop constantly. Any help to fix this issue is greatly appreciated.
It usually means the element is recreated, so you need to observe its ancestor higher up the DOM tree and add subtree: true.
For example, a parent element:
loadingObserver.observe(loading.parentElement, {attributes: true, subtree: true});
If this doesn't help at once you can try document.body first to make sure the mutation actually happens, then try various ancestor elements in-between to find the one that stays the same.
In the callback you'll need to verify that the mutation occurred on your desired element:
for (const m of mutations) {
if (m.target.id === 'foo') {
// it's the desired element, do something about it here and stop the loop
break;
}
}
I got this Code:
const odleglosc = parseFloat(document.getElementsByClassName("dystans")[0].innerText);
const daleko = "za daleko";
if (odleglosc > 200) {
alert(daleko);
}
<span data-pafe-form-builder-live-preview="lechnarlok" class="dystans" id="dystans">500</span>
It runs fine, because at starting point there is number higher than 200 in it.
But when i change it, alert don't trigger again..
How can i solve that? :(
Im not sure about how the span value will change, so this example works with an input. The same idea could also be applied to a span tho.
<input onchange="theFunction()" data-pafe-form-builder-live-preview="lechnarlok" class="dystans" id="dystans" value="500"></input>
<script>
function theFunction() {
var odleglosc = parseFloat(document.getElementsByClassName("dystans")[0].value);
var daleko = "za daleko";
if (odleglosc > 200)
{
alert(daleko);
}
}
</script>
Here, onChange calls the function whenever the value in the input field changes.
Do You want to show alert after each change of the value? If yes, use event listener for input (not for span).
Update:
Use MutationObserver for this case.
let span = document.getElementById('dystans');
function updateValue(value) {
var daleko = "za daleko";
if (parseFloat(value) > 200)
{
alert(daleko);
}
}
// create a new instance of 'MutationObserver' named 'observer',
// passing it a callback function
observer = new MutationObserver(function(mutationsList, observer) {
let value = mutationsList.filter(x => x.target.id =='dystans')[0].target.innerHTML;
updateValue(value);
});
// call 'observe' on that MutationObserver instance,
// passing it the element to observe, and the options object
observer.observe(span, {characterData: true, childList: true, attributes: true});
span.innerHTML = '3000';
<span data-pafe-form-builder-live-preview="lechnarlok" class="dystans" id="dystans">500</span>
Source:
Detect changes in the DOM
https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
Assuming I am unable to see below code, I have no idea how long does the timeout set, and I am unable to change the original code
document.querySelector('button').addEventListener('click', function(){
setTimeout(function(){
document.querySelector('.old').classList = 'old new';
}, 100);
});
<div class="old">OLD</div>
<button>Click</button>
What I'd like to achieve is that once the new class is added then I want to change the TEXT line, the hack code as below
document.querySelector('button').addEventListener('click', function(){
setTimeout(function(){
document.querySelector('.old').classList = 'old new';
}, 100);
});
document.querySelector('button').addEventListener('click', function(){
if(document.querySelectorAll('.new').length !== 0) {
document.querySelector('.old').innerText = "123"
}
});
<div class="old">OLD</div>
<button>Click</button>
document.querySelector('button').addEventListener('click', function(){
if(document.querySelectorAll('.new').length !== 0) {
document.querySelector('.old').innerText = "123"
}
});
Since I am unable to know how long the timeout secs, so my first click won't work as it executes right away. So I have to add the timeout seconds bigger than the original. Is there an efficient way to detected if new class is added?
or timeout is the best solution for this use case? Thanks
You might use Mutation Observer for this purpose:
document.querySelector('button').addEventListener('click', function() {
setTimeout(function() {
document.querySelector('.old').classList = 'old new';
}, 500);
});
document.querySelector('button').addEventListener('click', function() {
var observer = new MutationObserver(function(mutationsList, observer) {
for (var mutation of mutationsList) {
if (
mutation.type === 'attributes' &&
mutation.attributeName === 'class' &&
mutation.target.classList.contains('new')
) {
mutation.target.innerText = "123";
observer.disconnect();
}
}
});
observer.observe(document.querySelector('.old'), {attributes: !0});
});
<div class="old">OLD</div>
<button>Click</button>
Use ClassList API to get this done. As new class will be add after some time. If new class is not added to dom then you can't get reference of this node to compare. The ClassList provides a handy method to solve this issue. use contains method to check if new class added or not. If added then change the text.
use below code
document.querySelector('button').addEventListener('click', function(){
if(document.querySelectorAll('.old')[0].classList.contains("new")) {
document.querySelector('.old').innerText = "123"
}
});
Solution
I have a div, which will contain dropdowns and these dropdowns are created dynamically by the user on the click oo a button which is kept outside this div.
So what I need to achieve here is I wanna display 'No filter applied' when there are no dropdowns and remove that 'No filter applied' while there are dropdowns present.
I tried this scenario through addEventListener but I am not sure what action needs to implement for this scenario?
document.addEventListener("DOMContentLoaded", function(event) {
var activities = document.getElementById("dvContainer");
activities.addEventListener("change", function() {
if (activities.childElementCount > 0) {
activities.classList.add("displayZero");
} else {
activities.classList.remove("displayZero");
}
//console.log('ajay');
});
});
function AddDropDownList() {}
<input type="button" id="btnAdd" onclick="AddDropDownList()" value="Add Filter" />
<div id="dvContainer"><span>No Filters applied.</span></div>
This is my try, thanks in advance.
According to what you mentioned, you have a button that with click it, you add dropdowns dynamically.
so you don't need any extra event!!
in your button's click function:
yourButton.onclick=function(){
//..... do somethings similar adding dropdowns
activities.classList.add("displayZero");
};
And where you remove dropdown:
activities.classList.remove("displayZero");
Currently, I can think of only 2 ways to resolve:
The Easiest solution is first to create update function then call it from init of dom and then in the AddDropDownList. E.g.
function update() {
if (activities.childElementCount > 0) {
activities.classList.add("displayZero");
} else {
activities.classList.remove("displayZero");
}
}
window.onload = function() {
update();
}
function AddDropDownList() {
//Put Your code as you have written and then add
update();
}
Use Mutation Observer
window.onload = function() {
// Select the node that will be observed for mutations
var targetNode = document.getElementById('dvContainer');
// Options for the observer (which mutations to observe)
var config = {
attributes: true,
childList: true,
subtree: true
};
// Create an observer instance linked to the callback function
var observer = new MutationObserver(callback);
// Start observing the target node for configured mutations
observer.observe(targetNode, config);
}
function AddDropDownList() {
}
// Callback function to execute when mutations are observed
var callback = function(mutationsList) {
for (var mutation of mutationsList) {
if (mutation.type == 'childList') {
console.log('A child node has been added or removed.');
if (activities.childElementCount > 0) {
activities.classList.add("displayZero");
} else {
activities.classList.remove("displayZero");
}
} else if (mutation.type == 'attributes') {
console.log('The ' + mutation.attributeName + ' attribute was modified.');
}
}
};
I'm writing a jQuery plugin where the events which start/stop the plugin are customisable, so potentially the same event could both start and stop the plugin (e.g. click to start and click to stop).
What's an elegant way, ideally not involving timeouts or unbinding and rebinding of listeners (and not too many "isPlaying" "isBeingStarted" flags etc..) to make sure the correct callback is called
(Note: When I posted this answer, the question had a typo in it which made it seem like binding/unbinding would be okay as long as timeouts weren't involved.)
I don't see any need for timeouts, just bind/unbind as appropriate:
this.bind(startEvent, start);
function start() {
$(this).unbind(startEvent).bind(stopEvent, stop);
}
function stop() {
$(this).unbind(stopEvent).bind(startEvent, start);
}
In the above, I assume that startEvent is the configured start event name (and I'd probably add a namespace to it, e.g. the user passes in "click" but you add ".niftyplugin" to it resulting in startEvent containing "click.niftyplugin" so you can bind/unbind at will), and stopEvent is the configured stop event name (with namespace).
Here's a full example, with namespaces and using data to remember the options (you could use a closure if you prefer) - live copy:
// Plugin stuff
(function($) {
$.fn.niftyPlugin = niftyPlugin;
function niftyPlugin(options) {
var data;
data = {
startEvent: (options && options.startEvent || "click") + ".niftyplugin",
stopEvent: (options && options.stopEvent || "click") + ".niftyplugin"
};
this.data("niftyPlugin", data).bind(data.startEvent, start);
return this;
}
function start() {
var $this = $(this),
data = $this.data("niftyPlugin");
$this.unbind(data.startEvent).bind(data.stopEvent, stop);
display("Start");
}
function stop() {
var $this = $(this),
data = $this.data("niftyPlugin");
$this.unbind(data.stopEvent).bind(data.startEvent, start);
display("Stop");
}
function display(msg) {
$("<p>").html(msg).appendTo(document.body);
}
})(jQuery);
// Use
jQuery(function($) {
$("#theButton").click(function() {
$("<p>Non-plugin hook fired</p>").appendTo(document.body);
}).niftyPlugin({
startEvent: "click"
});
});
The only other alternative I see is stopImmediatePropagation - live example:
// Plugin stuff
(function($) {
$.fn.niftyPlugin = niftyPlugin;
function niftyPlugin(options) {
var startEvent, stopEvent, running = false;
startEvent = (options && options.startEvent || "click") + ".niftyplugin";
stopEvent = (options && options.stopEvent || "click") + ".niftyplugin";
this.bind(startEvent, start).bind(stopEvent, stop);
return this;
function start(event) {
if (running) {
return;
}
running = true;
display("Start");
event.stopImmediatePropagation();
}
function stop(event) {
if (!running) {
return;
}
running = false;
display("Stop");
event.stopImmediatePropagation();
}
}
function display(msg) {
$("<p>").html(msg).appendTo(document.body);
}
})(jQuery);
// Use
jQuery(function($) {
$("#theButton").click(function() {
$("<p>Non-plugin hook fired</p>").appendTo(document.body);
}).niftyPlugin({
startEvent: "click"
});
});
I don't like it, though, because it interferes with other handlers for the event. For instance, in the above, if I change the use to this:
// Use
jQuery(function($) {
$("#theButton").niftyPlugin({
startEvent: "click"
}).click(function() {
$("<p>Non-plugin hook fired</p>").appendTo(document.body);
});
});
...so the plug-in grabs the events before the non-plug-in code, boom, the non-plug-in code never sees the event (example).
So despite the overhead, I suspect bind/unbind are your friends here.
It may be overkill, but an elegant way to not have to maintain a bunch of flags (e.g. "isPlaying") is to use a Finite State Machine.
Here's a jQuery implementation: https://github.com/DukeLeNoir/jquery-machine
The eventual solution I've gone for is to do a quick uniqueness test for events used for stopping and starting and if there are any events used for both stopping and starting then a different listener (which does an isPlaying check) is attached to these. There's a small performance hit on loading the plugin, but after that the event handling code is about as efficient as can be.
function processEvents() {
var tempStart = opts.startEvent.split(" ").sort(),
tempStop = opts.stopEvent.split(" ").sort();
startEventLoop: for(var i=0, il = tempStart.length;i<il;i++) {
for(var j=0, jl = tempStop.length;j<jl;j++) {
if(tempStart[i] == tempStop[j]) {
stopStartEvents.push(tempStart[i])
tempStop.splice(j,1);
continue startEventLoop;
}
}
startEvents.push(tempStart[i])
}
startEvents = startEvents.join(" ");
stopEvents = tempStop.join(" ");
stopStartEvents = stopStartEvents.join(" ");
}
$this.on(stopEvents, function() {
$this.trigger("stop.flickBook");
}).on(startEvents, function() {
$this.trigger("start.flickBook");
}).on(stopStartEvents, function() {
playing ? $this.trigger("stop.flickBook") : $this.trigger("start.flickBook");
});