Does $.addClass not update the DOM immediately? - javascript

Consider the following snippet.
function foo(event) {
var $item = $(event.target);
$item.addClass("className");
bar(event);
};
function bar(event) {
var $item = $(event.target);
// The following return false
if ($item.hasClass("className")) {
// flow of control does not enter
}
};
I am not asking how to get around this. That would be plain obvious: to pass the reference to the $item to the bar function.
I am asking if jQuery:
Does or does not update the DOM immediately?
When does it update?
How can I make it update explicitly?
Finally, where can I read about this?

Does or does not update the DOM immediately?
Yes. Immediately.
When does it update?
The property/attribute of the DOM is updated immediately, you can read the changed value immediately. But the UI rendering may delay to the end of the JavaScript code (UI rendering doesn't matter).
How can I make it update explicitly?
You don't need to.
Finally, where can I read about this?
Well ... I think the DOM/ES standards may contain these contents.
And you can think about the "correct behavior" of a design: who can bear the strange behavior that can not read the changed value immediately? It must be crazy. So your browser always do the right things. :D
See your demo here, works without any problem (if it doesn't work, it is your browser's problem, maybe buggy version, or caused by some buggy plugins)
function foo(event) {
var $item = $(event.target);
$item.addClass("className");
bar(event);
};
function bar(event) {
var $item = $(event.target);
if ($item.hasClass("className")) {
$("#output").text("className changed!")
}
};
$(function(){
$("#test").on("click", foo);
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="test">click me</button>
<div id="output">hello</div>

your code should perform as expected, there's no apparent problem with it, but here's the thing that might be creating problems
when you do addClass, it triggers an even that updates the class='' attribute and removes your className, which is why the condition
($item.hasClass("className"))
returns false

Related

addEventListener("touchstart") doesn’t work on phones [duplicate]

I don't know what I am doing wrong but here is an example of what I am doing and it doesn't seem to work.
someDom.addEventListener('mousemove',function(ev) {self.onInputMove(ev)},false);
someDom.removeEventListener('mousemove',self.onInputMove);
The removeEventListener code is executed but it just doesn't remove the 'mousemove' listener
removeEventListener removes the listener that exactly matches the function that was added.
In this case, the function that addEventListener added was:
var some_func = function(ev) {
self.onInputMove(ev);
};
Store a reference to the actual function and you'll be good. So for example, the following should work:
someDom.addEventListener('mousemove',self.onInputMove,false);
someDom.removeEventListener('mousemove',self.onInputMove,false);
onInputMove is not an event-callback method. So you need to do something like:
var event = function(ev) {self.onInputMove(ev)};
someDom.addEventListener('mousemove', event,false);
someDom.removeEventListener('mousemove', event, false);
Why make it yourself so hard, just use the following to bind an event to an element:
element.onmousemove = function(e) {
// Some code here...
alert("Mouse moved!");
};
Now, when you want to remove the event, just do this:
element.onmousemove = null;
Done!
Hope this helps you guys out!
This page comes first on searching this/such issue on Google. So apart from answers already mentioned, here is one more interesting fact for future:
Leaving out the third optional variable in addEventListener() for useCapture/useBubble (as it defaults to false) does create some issue while removing the same eventlistener with same callback name. I faced this issue while working on chrome. Cant say about other browsers.
So do mention the third variable explicitly as "false".

Why does waitForKeyElements() only trigger once despite later changes?

For several years I've used the waitForKeyElements() function to track changes in webpages from a userscript. However, sometimes I've found it doesn't trigger as expected and have worked around out. I've run into another example of this problem, and so am now trying to figure out what the problem is. The following is the barest example I can create.
Given a simple HTML page that looks like this:
<span class="e1">blah</span>
And some Javascript:
// function defined here https://gist.github.com/BrockA/2625891
waitForKeyElements('.e1', handle_e1, false);
function handle_e1(node) {
console.log(node.text());
alert(node.text());
}
setInterval(function() {
$('.e1').text("updated: "+Math.random());
}, 5000);
I would expect this code to trigger an alert() and a console.log() every 5 seconds. However, it only triggers once. Any ideas?
Here's a codepen that demonstrates this.
By design and default, waitForKeyElements processes a node just once. To tell it to keep checking, return true from the callback function.
You'll also want to compare the string (or whatever) to see if it has changed.
So, in this case, handle_e1() would be something like:
function handle_e1 (jNode) {
var newTxt = jNode.text ();
if (typeof this.lastTxt === "undefined" || this.lastTxt !== newTxt) {
console.log (newTxt);
this.lastTxt = newTxt;
}
return true; // Allow repeat firings for this node.
}
With the constant string comparisons though, performance might be an issue if you have a lot of this on one page. In that scenario, switching to a MutationObserver approach might be best.

JavaScript (jquery) - Scope issue in click event

I know this subject has been already discussed in similar topics, but none of the solutions I could find can help me understand the issue I have.
Here is my simplified class and its the usual was I define them.
BottomNav = function() {
this.init();
}
$.extend(BottomNav.prototype, {
init: function(){
this.insue = false;
$(".up").click($.proxy(function () {
var thisinuse = this.inuse;
if(this.inuse===false) {
this.inuse = true;
this.moveSlider('up');
}
},this));
},
moveSlider: function(d){
//some instructions
alert('move slider');
}
});
$(document).ready(function() {
new BottomNav();
});
In FireBug on the breakpoint inside the click event this.inuse is undefined! (this is my problem), my scope looks good on the watch (right panel of firebug), this.insue is false as it should be - sorry I cannot post images yet!
I would be grateful of someone might help identifying this very strange behavior.
I tried some staff like putting the click event definition inside another function but it does not work either. I tried different ways of bindings and it does not work too.
However the below example is working on a number of other classes I made. I could access class scope from events, effects.
It's just a typo:
this.insue = false;
Change insue to inuse and the property will be there :-)
Apart from that, the variable thisinuse is quite superfluous in here. And change the condition to if(! this.inuse) instead of comparing to booleans…
this.inuse can be assigned to a variable out side your click event handler and use the variable inside the handler.

setTimout screws up variable javascript

I'm having trouble with a piece of script that removes an object X amount of time after it has gotten the class 'hidden'
selector = getselector($(this).parent().parent());
console.log("Clicked Cancel");
$(this).parent().parent().addClass('hidden');
setTimeout(function() {
$(selector).remove();
}, 400);
I edited some piece of script from here to make function getselector since $(this) doesn't work within a setTimeout.
now this piece of code works, as long as you don't run it too quickly again.
problem seems to be that variable selector gets messed up when a another node gets deleted within the timespan (currently 400ms)
and I can't think of an easy way around it. :(
The answer is simple: Don't make selector global, i.e. use var. Oh, and simply store the element instead of trying to build a selector:
var elem = $(this).parent().parent();
elem.addClass('hidden');
setTimeout(function() {
elem.remove();
}, 400);
You can also queue the removal in the following way, which makes your Code a little bit more spicy:
$(this).parent().parent().addClass('hidden').delay(400).queue(function() {
$(this).remove();
});
Set a variable with a value true at the start of the process. On action, check whether it is not false and then set it to false and then back to true once finished. If you click it too fast, it will check your variable, see it is true again and will do the action again.

Creating a jQuery plugin to cause individual elements to react when the window is resized. How do I handle the listeners?

I'm working on a plugin that causes something to happen in selected elements when the window is resized. The logic on what happens is perfectly fine, but I'm running into a conundrum when it comes to where to hook on the event listeners.
Basically, the code looks something like this:
$.fn.beAwesome = function() {
return this.each(function(){
// ???
});
}
While I've completed the rest of the plugin (and have it working just fine on, say, a click even on this), I can't put it "all together" without solving this central issue.
I considered just adding an extra bind resize to $(window) for each awesome element, but there'd be no way to access the element from within the closure:
$.fn.beAwesome = function() {
$window = $(window);
return this.each(function(){
$window.resize(function(){
// can't access initial awesome element
});
});
}
The other solution that sprung to mind was to instantiate a global data store (probably in $(document).data('beAwesome') or something like that. This, however, doesn't seem Javascript-like, blocking off access once the function runs its course, so I'd have to come up with some roundabout ways to do things like removing the hook. I've definitely seen approaches like these in Javascript libraries I've used in the past, but I don't know whether that's due to necessity or laziness on the authors' parts.
So is there an effective way to accomplish what I'm looking for?
You shouldn't bind to the resize event multiple times, will just end up with bloat and slowness.
What you could do is create a var to store all the elements that are to beAwesome. Bind to the resize event once. And then on resize do something will all the elements that are in the var.
(function($){ // closure to keep your pluggin contained.
var elems = $([]);
$(window).bind('resize.beAwesome', function(){
if (!elems.length) return; //no need to continue if elems in empty.
// Do something with elems here.
});
$.fn.beAwesome = function() {
elems = elems.add(this);
return this;
}
})(jQuery);
You also might want to namespace your resize event.

Categories