Is there a way to have javascript coffee always be checking the document? What I mean by this is if I have "if" statements, it will always check the document to see if it needs to run the script...
Also, it needs to not be a function that needs to be called. Here is some simple example of what I'm asking.
<button id="btn1">random button</button>
<button id="btn2" onclick="hide()">hide</button>
<script>
function hide() {
document.getElementById("btn1").setAttribute("hidden", "true")
}
</script>
<script>
if (document.getElementById("btn1").hidden === true) {
document.write("random button is hidden")
}
</script>
How would I do something like this? I don't want this specifically, this is just an example.
In the code,a button hides another one, and there is script always checking if it is hidden. Please let me know.
edit
I see other people's answers, but it's not what I need. Is it possible to have functions constantly run without being called?
JavaScript features what's called events. Any DOM element may emit events in response to user interaction. For example:
var button2 = document.querySelector('#btn2');
// what event function to be called
// vvvvvvv vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
button2.addEventListener('click', function runThisOnButtonClick(event) {
// Code in here will only run when the button is clicked.
hideInSomeFashion(button1);
});
If you want a more general view of the DOM, recent browsers have what's called a MutationObserver.
In your case, something like this [example]:
// Older versions of Webkit have this under a prefix
var MutationObserver = MutationObserver || WebkitMutationObserver;
var myMo = new MutationObserver(function(mutations) {
// handleMutation in this case is a function that takes
// a mutation and performs an action. Note that in this
// case, return values are ignored.
mutations.forEach(handleMutation);
});
myMo.observe(button1, {
attributes: true // Observe changes in attributes
});
function handleMutation(mutation) {
console.log("Hidden?", mutation.target.hidden);
}
MutationObserver can observe a common ancestor and all of its descendants for changes as well.
Although I strongly recommend finding another way to do what you want, if possible, I think this is what you're asking for.
setInterval(function() {
if(document.getElementById("btn1").hidden === true) {
// Button is hidden
}
}, 1000); // Wait 1000ms before running again
Related
How can I detect when a new element has been added to the document in jquery ?
Explanation:
I want to know when an element with class "column-header" has been added to the document. As I plan to run some javascript on those elements.
How can I do this ? I use jQuery javascript library.
The accepted answer uses an obsolete plugin from 2011 and the highest upvoted answer uses Mutation events which are now deprecated.
Today, a MutationObserver is what you should use to detect when an element has been added to the DOM. MutationObservers are now widely supported across all modern browsers (Chrome 26+, Firefox 14+, IE11, Edge, Opera 15+, etc).
Here's a simple example of how you can use a MutationObserver to listen for when an element is added to the DOM.
For brevity, I'm using jQuery syntax to build the node and insert it into the DOM.
var myElement = $("<div>hello world</div>")[0];
var observer = new MutationObserver(function(mutations) {
if (document.contains(myElement)) {
console.log("It's in the DOM!");
observer.disconnect();
}
});
observer.observe(document, {attributes: false, childList: true, characterData: false, subtree:true});
$("body").append(myElement); // console.log: It's in the DOM!
The observer event handler will trigger whenever any node is added or removed from the document. Inside the handler, we then perform a contains check to determine if myElement is now in the document.
You don't need to iterate over each MutationRecord stored in mutations because you can perform the document.contains check directly upon myElement.
To improve performance, replace document with the specific element that will contain myElement in the DOM.
$(document).bind('DOMNodeInserted', function(e) {
console.log(e.target, ' was inserted');
});
DOMNodeInserted is a DOM level 3 Event. That in turn means, you need a fairly new browser to support that kind of event.
Reference: MDC
If you want to do some jQuery on it, you can also do something like livequery (extra plugin):
$('column-header').livequery(function()
{
// do things here with your column-header, like binding new events and stuff.
// this function is called when an object is added.
// check the API for the deletion function and so on.
});
UPDATE:
It seems that the link was broken. Try this link
I would use setInterval to repeatedly check for element. eg.
var curLength=0;
setInterval(function(){
if ($('.column-header').length!=curLength){
curLength=$('.column-header').length;
// do stuff here
}
},100);
this code will check for any new .colum-header elements every 100 ms
I think the DOMNodeInserted method mentioned above is probably the sexiest choice, but if you need something that is going to work with IE8 and lower, you could do something like the following:
// wrap this up in en immediately executed function so we don't
// junk up our global scope.
(function() {
// We're going to use this to store what we already know about.
var prevFound = null;
setInterval(function() {
// get all of the nodes you care about.
var newFound = $('.yourSelector');
// get all of the nodes that weren't here last check
var diff = newFound.not(prevFound);
// do something with the newly added nodes
diff.addClass('.justGotHere');
// set the tracking variable to what you've found.
prevFound = newFound;
}, 100);
})();
That is just the basic idea, you can change that however you want, make a method out of it, keep the return value of the setInterval so you can stop it, or whatever you need to do. But it should get the job done.
You should check out the jQuery Mutation Events. I believe that it is what you are looking for.
if you want to set same functions for all the elements with class column-header
Then you may use jquery live()
$(".column-header").live("click",function(){});
How about using a poll - keep searching for an element with class "column-header", until you find it and then perform action. Seems simple.
Up to 2013 MutationEvents are deprecated. If anyone else has got problems detecting new elements in the dom, you can use MutationObservers Instead:
https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
Just change this function call (observing).
Parameter 1 - Which element to monitor
Parameter 2 - Which element will trigger the function (parameter 3) when appears.
Parameter 3 - Function triggered
observing('div#application', 'div.viewContainer', function(mutation){
console.log('Mutation:', mutation)
})
<html>
<head>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script>
/* Observing Easy */
function observing(who, forWhat, callBack){
var observer = new MutationObserver(function(mutations) {
mutations.forEach(mutation => {
console.log(mutation.addedNodes[0])
if ( mutation.addedNodes[0].matches(forWhat)){
console.log('MATCH!');
if(callBack!==undefined){
callBack(mutation)
}
}else{
console.log('%cNOT match', 'color: #f00');
}
});
});
observer.observe($(who)[0], {attributes: false, childList: true, characterData: false, subtree:false});
}
$(document).ready(function(){
/* Observing Usage */
observing('div#application', 'div.viewContainer', function(mutation){
console.log('Mutation:', mutation)
})
/* Adding Elements for Test */
$("#application").append($("<div>Hello World</div>")[0]);
$("#application").append($("<div class='contentFake'>ContentClassFAKE</div>")[0])
$("#application").append($("<div class='contentFake'>ContentClass1</div>")[0])
$("#application").append($("<div class='viewContainer'>ContentClass2</div>")[0])
setTimeout(function(){
$("#application").append($("<div class='contentFake'>ContentClass3</div>")[0])
}, 1000)
setTimeout(function(){
$("#application").append($("<div class='viewContainer'>ContentClass4</div>")[0])
}, 1000)
})
</script>
</head>
<body>
<div id="application"></div>
</body>
</html>
try this...
if($('.column-header')){
//do something...
}
or you can also do something like this..it will be more generic
jQuery.exists = function(selector) { return ($(selector).length > 0); }
if ($(selector).exists()) {
// Do something
}
Scenario:
We have a MutationObserver handler function handler.
In handler, we do some DOM manipulation that would trigger handler again. Conceptually, we would have a reentrant handler call. Except MutationObserver doesn't run in-thread, it will fire after the handler has already finished execution.
So, handler will trigger itself, but through the async queue, not in-thread. The JS debugger seems to know this, it will have itself as an async ancestor in the call stack (i.e. using Chrome).
In order to implement some efficient debouncing of events, we need to detect same; that is, if handler was called as a result of changes triggered by itself.
So how to do?
mutationObserver=new MutationObserver(handler);
mutationObserver.observe(window.document,{
attributes:true,
characterData:true,
childList:true,
subtree:true
});
var isHandling;
function handler(){
console.log('handler');
// The test below won't work, as the re-entrant call
// is placed out-of-sync, after isHandling has been reset
if(isHandling){
console.log('Re-entry!');
// Throttle/debounce and completely different handling logic
return;
}
isHandling=true;
// Trigger a MutationObserver change
setTimeout(function(){
// The below condition should not be here, I added it just to not clog the
// console by avoiding first-level recursion: if we always set class=bar,
// handler will trigger itself right here indefinitely. But this can be
// avoided by disabling the MutationObserver while handling.
if(document.getElementById('foo').getAttribute('class')!='bar'){
document.getElementById('foo').setAttribute('class','bar');
}
},0);
isHandling=false;
}
// NOTE: THE CODE BELOW IS IN THE OBSERVED CONTENT, I CANNOT CHANGE THE CODE BELOW DIRECTLY, THAT'S WHY I USE THE OBSERVER IN THE FIRST PLACE
// Trigger a MutationObserver change
setTimeout(function(){
document.getElementById('asd').setAttribute('class','something');
},0);
document.getElementById('foo').addEventListener('webkitTransitionEnd',animend);
document.getElementById('foo').addEventListener('mozTransitionEnd',animend);
function animend(){
console.log('animend');
this.setAttribute('class','bar-final');
}
#foo {
width:0px;
background:red;
transition: all 1s;
height:20px;
}
#foo.bar {
width:100px;
transition: width 1s;
}
#foo.bar-final {
width:200px;
background:green;
transition:none;
}
<div id="foo" ontransitionend="animend"></div>
<div id="asd"></div>
Note
Our use case comprises of 2 components here; one we will call contents which is any run-of-the-mill web app, with a lot of UI components and interface. And an overlay, which is the component observing the content for changes and possibly doing changes of its own.
A simple idea that is not enough is to just disable the MutationObserver while handling; or, assume every second call to handler as recursive; This does not work in the case illustrated above with the animationend event: the contents can have handlers which in turn can trigger async operations. The two most popular such issues are: onanimationend/oneventend, onscroll.
So the idea of detecting just direct (first-call) recursion is not enough, we need quite literally the equivalent of the call stack view in the debugger: a way to tell if a call (no matter how many async calls later) is a descendant of itself.
Thus, this question is not limited to just MutationObserver, as it necessarily involves a generic way to detect async calls descendent of themselves in the call tree. You can replace MutationObserver with any async event, really.
Explanation of the example above: in the example, the mutationobserver is triggering the bar animation on #foo whenever #foo is not .bar. However, the contents has an transitionend handler that sets #foo to .bar-final which triggers a vicious self-recursion chain. We would like to discard reacting to the #foo.bar-final change, by detecting that it's a consequence of our own action (starting the animation with #foo.bar).
One possible workaround for this could be to stop the mutation observer when one mutation is being fired
mutationObserver=new MutationObserver(handler);
mutationObserver.observe(window.document,{
attributes:true,
characterData:true,
childList:true,
subtree:true
});
// Trigger a MutationObserver change
document.getElementById('foo').setAttribute('class','bar');
document.getElementById('foo').setAttribute('class','');
function handler(){
console.log('Modification happend')
mutationObserver.disconnect();
// Trigger a MutationObserver change
document.getElementById('foo').setAttribute('class','bar');
document.getElementById('foo').setAttribute('class','');
mutationObserver.observe(window.document,{
attributes:true,
characterData:true,
childList:true,
subtree:true
});
}
See the JS fiddle
https://jsfiddle.net/tarunlalwani/8kf6t2oh/2/
The way I have done this in the past is to create semaphore to flag when an async function has already been called and is waiting execution on the event loop.
Here is a simple example for requestAnimationFrame
import raf from 'raf'
import State from './state'
let rafID
function delayedNotify () {
rafID = null
State.notify()
}
export default function rafUpdateBatcher () {
if (rafID) return // prevent multiple request animation frame callbacks
rafID = raf(delayedNotify)
}
In this case once you call the function, all future calls will be ignored until the first one is executed. Think of it as an async throttle function
For more complex scenarios then another solution might be this project
https://github.com/zeit/async-sema
From what I gather from reading your comments, if action A triggered action B asynchronously, you want to be able to tell where action A was done (in general, not just in a mutation observer). I don't think there's any trick built into JavaScript to do this like it seems you're looking for, however, if you know exactly how your JavaScript works, you can track this information. Job queues in JavaScript are FIFO by definition, the event loop queue also works this way. That means you can store information corresponding to a specific event in an array at the same time you're doing the action which triggers the event, and be confident that they're getting processed in the same order as the array. Here's an example with your mutation observer.
const
foo = document.getElementById('foo'),
mutationQueue = [];
function handler(){
console.log('handler');
const isHandling = mutationQueue.shift();
if(isHandling){
console.log('Re-entry!');
// Throttle/debounce and completely different handling logic
return;
}
setTimeout(()=> {
foo.setAttribute('class','bar');
foo.setAttribute('class','');
mutationQueue.push(true);
}, 1000 * Math.random());
}
mutationObserver=new MutationObserver(handler);
mutationObserver.observe(foo, {
attributes:true,
characterData:true,
childList:true,
subtree:true
});
function randomIntervals() {
setTimeout(()=>{
foo.setAttribute('class','bar');
foo.setAttribute('class','');
mutationQueue.push(false);
randomIntervals();
}, 1000 * Math.random())
}
randomIntervals();
<div id='foo'></div>
You have to make sure you add the appropriate value to the array at every point in your code that is going to trigger your async handler, or the entire thing will be messed up. I've never done this myself, I just thought of it for this question, but it seems pretty easy to do wrong. However, I fear this may be the only way to do what you want in general.
onclick="HandleAction(\'playnow\');HandleAction(\'stop\');"
Performs the two functions simultaneously. No good since it just does the action \playnow\ and then \stop\ immediately after.
I need onClick to perform the function every other click.
Click 1: \playnow\
Click 2: \stop\
Click 3: \playnow\
etc.
Is there a simple way to achieve this?
Define a var which holds the current state:
var state = false;
function handleAction() {
if (state == false) {
// stuff for 'playnow' action
state = true;
return;
}
if (state == true) {
// stuff for 'stop' action
state = false;
return;
}
}
Declare a global variable and interchange it depending on what is passed:
var switch = "playnow";
function handleAction(a) {
if a === switch {
alert(a);
}
else {
alert(a);
switch = a;
}
}
See my answer to this question.
Lets make a function called toggle that lets you specify actions that happen every other click
var toggle = function (a, b) {
var togg = false;
return function () {
// passes return value back to caller
return (togg = !togg) ? a() : b();
};
};
We would then setup our click handler like this
button.addEventListener('click', toggle(function () {
//play now logic
}, function () {
// stop logic
}));
Your click handler now alternates between the first and second functions with every click.
Yes. You could do something like
var play=false,
button = document.getElementById('playbtn'),
audiotag = document.getElementById('audio');
button.addEventListener('click', function(){
if(play){
audiotag.pause()
play=true;
}else{
audiotag.play();
play=false;
}
})
to make this work you could use html like this:
<div id="audioplayer">
<button id="playbtn">play</button>
<audio id="audiotag" src="path/to/audiofile"></audio>
</div>
so you would add the audio html like above and use getElementById to get each element in javascript. after that you attach an event listener to listen for the click event and call the handler which is the anonymous function. Inside that function, you can use the native play and pause methods directly on the audio object to stop audio when it's playing and then play it again when it's stopped.
there are other attributes you can add to the audio tag to start it playing as soon as the page loads. When you click the button, the play variable is set to true so it will pause on the first click and then sets that to false. A subsequent click will play it again and set the variable to true again and so on
Tyr gave you an answer how to solve your problem. Here you go some notes which can help you design better code.
If you have i.e. one big animation and one button on your web, your code is perfectly ok, to keep the code simple is a good idea. But if you have something like this
<button onclick="...">Animation A</button>
<button onclick="...">Animation B</button>
Then you need better design. If you insert state global var into HandleAction, you break low coupling, HandleAction is bound to your single event and can't be reused elsewhere.
It is good to ask yourself What does this function do? In the first case, it is useful to choose better name, like HandleIntroAnimation. If it handles (any) animation, then it is good to specify it in the parameter.
function HandleAnimation(animation, play) {
if(play) ... // playnow
else ... // stop
}
This indeed does what the name tells. To use it in your code, write a proxy function:
<script>
// If you enhance your web, you only alter this code.
// HandleAnimation stays the same, regardless the changes.
var animationStatus = {}
function ToggleAnimation(animation) {
animationStatus[animation] = !animationStatus[animation];
HandleAnimation(animation, animationStatus[animation]);
}
</script>
<button onclick="toggleAnimation(this)">Animation A</button>
<button onclick="toggleAnimation(this)">Animation B</button>
Finally, you could completely decouple HTML and JS:
animations.js
window.addEventListener("load",function() {
// HandleAnimation and ToggleAnimation definitions goes here
// to avoid collisions in global namespace
var anims = document.getElementsByClassName("animation");
for(var i=0; i<anims.length; ++i) anims[i].addEventListener("click",function() {
ToggleAnimation(anims[i]);
});
});
your html
<script src="animations.js"></script>
<button class="animation">Animation A</button>
<button class="animation">Animation B</button>
and you have animation framework: every element with animation class magically toggles its animation. The animation data could be provided in data-* attribute, data-animation in this case.
Then you can provide it as open-source on github or use someone elses open code to fill the missing parts in your code where you were too lazy to code it yourself. Since many wheels were already invented, the only thing you need to code is usually proxy functions. That's how coders save each others time. Happy coding.
This always gets me. After initializing all lovely UI elements on a web page, I load some content in (either into a modal or tabs for example) and the newly loaded content does not have the UI elements initialized. eg:
$('a.button').button(); // jquery ui button as an example
$('select').chosen(); // chosen ui as another example
$('#content').load('/uri'); // content is not styled :(
My current approach is to create a registry of elements that need binding:
var uiRegistry = {
registry: [],
push: function (func) { this.registry.push(func) },
apply: function (scope) {
$.each(uiRegistry.registry, function (i, func) {
func(scope);
});
}
};
uiRegistry.push(function (scope) {
$('a.button', scope).button();
$('select', scope).chosen();
});
uiRegistry.apply('body'); // content gets styled as per usual
$('#content').load('/uri', function () {
uiRegistry.apply($(this)); // content gets styled :)
});
I can't be the only person with this problem, so are there any better patterns for doing this?
My answer is basically the same as the one you outline, but I use jquery events to trigger the setup code. I call it the "moddom" event.
When I load the new content, I trigger my event on the parent:
parent.append(newcode).trigger('moddom');
In the widget, I look for that event:
$.on('moddom', function(ev) {
$(ev.target).find('.myselector')
})
This is oversimplified to illustrate the event method.
In reality, I wrap it in a function domInit, which takes a selector and a callback argument. It calls the callback whenever a new element that matches the selector is found - with a jquery element as the first argument.
So in my widget code, I can do this:
domInit('.myselector', function(myelement) {
myelement.css('color', 'blue');
})
domInit sets data on the element in question "domInit" which is a registry of the functions that have already been applied.
My full domInit function:
window.domInit = function(select, once, callback) {
var apply, done;
done = false;
apply = function() {
var applied, el;
el = $(this);
if (once && !done) {
done = true;
}
applied = el.data('domInit') || {};
if (applied[callback]) {
return;
}
applied[callback] = true;
el.data('domInit', applied);
callback(el);
};
$(select).each(apply);
$(document).on('moddom', function(ev) {
if (done) {
return;
}
$(ev.target).find(select).each(apply);
});
};
Now we just have to remember to trigger the 'moddom' event whenever we make dom changes.
You could simplify this if you don't need the "once" functionality, which is a pretty rare edge case. It calls the callback only once. For example if you are going to do something global when any element that matches is found - but it only needs to happen once. Simplified without done parameter:
window.domInit = function(select, callback) {
var apply;
apply = function() {
var applied, el;
el = $(this);
applied = el.data('domInit') || {};
if (applied[callback]) {
return;
}
applied[callback] = true;
el.data('domInit', applied);
callback(el);
};
$(select).each(apply);
$(document).on('moddom', function(ev) {
$(ev.target).find(select).each(apply);
});
};
It seems to me browsers should have a way to receive a callback when the dom changes, but I have never heard of such a thing.
best approach will be to wrap all the ui code in a function -even better a separate file -
and on ajax load just specify that function as a call back ..
here is a small example
let's say you have code that bind the text fields with class someclass-for-date to a date picker then your code would look like this ..
$('.someclass-for-date').datepicker();
here is what i think is best
function datepickerUi(){
$('.someclass-for-date').datepicker();
}
and here is what the load should look like
$('#content').load('/uri', function(){
datepickerUi();
})
or you can load it at the end of your html in script tag .. (but i dont like that , cuz it's harder to debug)
here is some tips
keep your code and css styles as clean as possible .. meaning that for text fields that should be date pickers give them one class all over your website ..
at this rate all of your code will be clean and easy to maintain ..
read more on OOCss this will clear what i mean.
mostly with jquery it's all about organization ... give it some thought and you will get what you want done with one line of code ..
edit
here is a js fiddle with something similar to your but i guess it's a bit cleaner click here
How would I make a start button for a function? I have 9 different functions for different animations on my page. I need to figure out how execute the animations only when a button is clicked(a start button) The reason why I want to do this is because I'm making a simple game, but I'd like the end user to be able to interact with the elements of the game before they start(I already have this done with jQuery, but at the moment I can only move the elements while the game is running which isn't what I want to do.) A quick example of the animate function is
function animate0(pos) {
pos %= urls.length;
var animation0 = document.getElementById('animation0');
var counter = document.getElementById('counter');
animation0.src = urls[pos];
if (pos == 1) {
animation0.onclick = function() {
counter.innerHTML = parseInt(counter.innerHTML) + 1;
}
}
else {
animation0.onclick = function() {
//do nothing
}
}
setTimeout(function() {
animate0(++pos);
}, (Math.random()*500) + 1000);
}
Then to execute the animation I use this
window.onload = function() { //Frames go below, seperated by commas format= , "URL");
urls = new Array("http://i51.tinypic.com/sxheeo.gif", "http://i56.tinypic.com/2i3tyw.gif");
animate0(0);
To display the animation on the page,
<img id='animation0' src ='http://i51.tinypic.com/sxheeo.gif'/>
Thanks!
document.getElementById('start').onclick = function(){
animate0(0);
}
This is assuming you have an element with id='start'
Here is a fiddle: http://jsfiddle.net/maniator/TQqJ8/13/
I think I may be misunderstanding your question, but if for instance you had this button (or indeed just about any other element):
<input type="button" id="theButton" value="Click Me">
Then you can hook up a handler for its click event in any of several ways.
The simplest, but least powerful, is DOM0-style:
document.getElementById("theButton").onclick = function() {
animate0(0);
return false;
};
The problem with DOM0 handlers is that there can only be one handler/event type on each element.
DOM2-style, which on standards-based browsers looks like this:
document.getElementById("theButton").addEventListener('click', function(event) {
event.preventDefault();
animate0(0);
}, false);
...and on older IE looks like this:
document.getElementById("theButton").attachEvent('onclick', function() {
animate0(0);
return false;
});
Note the differences, the event name is "click" in the DOM2 standard, "onclick" for Microsoft, and the event object is passed in the first argument in the DOM2 standard, but it's a global variable (I didn't use) on Microsoft's older browsers. (Not dissing Microsoft, in the IE5 - IE6 timeframe, they did a lot to innovate web browsers, including ajax, innerHTML, and in fact multiple handlers per event per element via attachEvent, which predates the DOM2 standard. It's just after IE6 that they let the ball drop for...well...more than a decade.)
In all of those cases, I've called animate0 directly, since it does its first loop and then schedules the next bit itself. You could, of course, use setTimeout to schedule even the first bit to be asynchronous (probably just use a delay of 0, which will be 5-10 ms on most browsers). It depends on what you want to do.