jQuery executing both branches of an if-else statement in Rails - javascript

I have the following jQuery on a Rails page:
$(document).on('click','.reportsArrow', function() {
if ( $(this).parent().hasClass('reportCollapsed') ) {
console.log("A");
$(this).parent().removeClass('reportCollapsed');
}else{
$(this).parent().addClass('reportCollapsed');
console.log("B");
}
});
When I click on an element with reportsArrow and without reportCollapsed, the log shows
B
A
Meaning it is executing the else part and then executing the if part. I want the function to only be executed once per click, and to only follow one code path. Why is it being executed twice and how do I stop this? I should point out that this toggles correctly in the mockups created by the web designer (on HTML/CSS/JS only). It looks like the problem is Rails related.
EDIT:
We have found a working solution:
$('.reportsArrow').click(function() {
$(this).parent().toggleClass('reportCollapsed');
});

The event would be getting fired more then once and propagated up-ward in the DOM tree. Use event.stopPropagation(). You can also use the toggleClass instead of branching.
$(document).on('click','.commonClass', function(event) {
event.stopPropagation();
$(this).parent().toggleClass('newClass');
});

Not sure why, but my days in unobtrusive javascript have taught me to be as specific and as least fuzzy as I can.
Never worried why, as long as it worked. Having been asked why (just here), my answer is "I will have to look it up". Sorry.
Thus, I would avoid setting a catch method on THE document and then filter actions: I would directly point the event catches on the element (or set of elements) I want to watch.
So, instead of using:
$(document).on('click','.reportsArrow', function() {
//...
});
I would go the direct way:
$('.reportsArrow').click(function () {
//..
});
Having read the API documentation for jQuery .on(), it appears to me that it would be probably more suitable to use .one() instead, so there is no continuation after hit "#1". But I have not tested it, so I can't say for sure.

You need to stop event propogation to child elements.also you can use toggleClass instead:
$(document).on('click','.commonClass', function(e) {
e.stopPropagation();
$(this).parent().toggleClass('newClass')
});

Try this,
You need to avoid event bubbling up the DOM tree. There must be a parent causing the event to fire twice or more time.
To avoid this use event.stopPropagation()
$(document).on('click','.commonClass', function(event) {
event.stopPropagation();
$(this).parent().toggleClass('newClass');
});

I could not reproduce your problem. Your code is working fine in my Firefox on a simple HTML page.
Please try this piece of code and come back with the console output:
function onClick(ev) {
console.log(ev.currentTarget, '\n', ev.target, '\n', ev);
if(ev.target === ev.currentTarget)
console.log($(this).parent().toggleClass('newClass').hasClass('newClass') ? 'B' : 'A');
};
EDIT:
and of course:
$(document).on('click', '.commonClass', onClick);

For readability put the logic into the jQuery selector using the :not like this
$(document).on('click','.reportCollapsed > .reportsArrow', function() {
$(this).parent().removeClass('reportCollapsed')
console.log("A");
})
$(document).on('click','not:(.reportCollapsed) > .reportsArrow', function() {
$(this).parent().addClass('reportCollapsed')
console.log("B");
})

Given that this works one time (click > else > B) could it be that something listens for DOMSubtreeModified or other DOMChange Events which again trigger a click on the document ?
Have you tried debugging/following the calls after the inital click? Afaik chrome has a nice gui to do this.

Related

How to raise an event click, have fields added to Ajax?

I have code that works correctly
$(document).on('click',"a.img,a.imgs",function() {
$(this).next().find('a:first').click();
return false;
});
But when I add new fields ajax ( for example show more), then with them this code does not work, and it's sad
Edited my answer as I misread your code and got everything mixed up.
Here's an explanation from another SO thread that might help you fix the problem:
It's probably not working due to one of:
Not using recent version of jQuery
Not wrapping your code inside of DOM ready
or you are doing something which causes the event not to bubble up to the listener on the document.
$(document).ready(function() {
// This WILL work because we are listening on the 'document',
// for a click on an element with an ID of #test-element
$(document).on("click","#test-element",function() {
alert("click bound to document listening for #test-element");
});
// This will NOT work because there is no '#test-element' ... yet
$("#test-element").on("click",function() {
alert("click bound directly to #test-element");
});
// Create the dynamic element '#test-element'
$('body').append('<div id="test-element">Click mee</div>');
});
$(document).on("click"... not working?

Return false not working sometimes

I have a page with photo gallery http://dev.dolina-imeniy.ru/fotogalereya/kp_usadba_tishnevo/
I use this to bind click event and return it false
$('a.link_photo').click(function(e) {
var new_img = $(this).attr('href');
var photo_title = $(this).attr('title');
var photo_info = $('.photo_info', this).html();
$('#photo_view img').attr({
src : new_img
});
$('#photo_title').html(photo_title);
$('#photo_info').html(photo_info);
return false;
});
But on some images it not work! Why it appears?
Try click on 10 image (ut-1-foto.jpg) in my example to see it.
For some reason you code is breaking, so it does not reach to return false.
You can use e.preventDefault(); to stop the default action
e.preventDefault();
The reason for this is that the function only binds to the elements that are already in existent when it is called. Every link created after the the document has loaded will not be bound to this function. To listen for the creation of these elements and to then bind the function to them, you could use the jQuery plugin liveQuery. I hope that helps.
trying calling e.preventDefault()
For more info look here:
http://api.jquery.com/event.preventDefault/
I don't think return false; or event.preventDefault() has anything to do with it. I'm guessing it has to do with how your carousel works. It's no coincidence that your code breaks once the images start repeating - the click event is probably no longer bound. If the element is just being moved, the events should still be set, but if it's being cloned or copied the events might not be.
edit: I can confirm by debugging that your script isn't even called on the 'broken' links.

Jquery: bind load + change simultaneously

In many cases, I need to bind a behaviour to an element after loading, and then after an event triggering (like "change").
I think the best way would be to make it in the same line:
$('#element_id').bind('load, change', function () {
...
});
But this works only for "change" and not for "load". There is a better way?
I stumbled across the same problem. Removing comma is not enough, at least not in this case:
$(document).ready(function(){
$('#element_id').bind('load change', function () {
... // (this DOESN'T get called on page load)
});
});
I guess load events get triggered before $(document).ready().
This is a simple solution:
$(document).ready(function(){
$('#element_id').bind('change', function () {
...
});
$('#element_id').trigger('change');
});
For already loaded content, when you want to run a function on an event and also straight away, you can use a custom event of your own naming to avoid triggering any existing bindings from libraries etc. on the built in events, e.g.
$(".my-selector").on("change rightnow", function() {
// do stuff...
}).triggerHandler("rightnow");
Don't you just need to remove the comma?
try it without the comma:
$('#element_id').bind('load change', function () {
...
});
http://api.jquery.com/bind/#multiple-events

After jQuery ajax load or update, I lose the mouseover event

After using .load to update my div, that is add item to my list, I used firebug and saw that the list was updated. However, I lost the mouseover event that worked when the page first loaded. In my script js I have:
// hide and show are css classes that display none and block respectively
function openList(){
$("#miniList").removeClass().addClass("show");
}
function closeList(){
$("#miniList").removeClass().addClass("hide");
}
...
$(document).ready(function() {
$("#miniList").mouseover(function() {
openList();
})
$("#miniList").mouseout(function() {
closeList();
})
});
function addItemToDiv(id, ref, num) {
$("#miniList").load("/list/ajax_updateList.jsp", {
'action' : 'additem',
'pid' : id,
'pref' : ref,
'qty' : num
});
}
Of course, this works fine the first time the page is loaded, but when I add item to the list, DOM is update but mouseover effects don't work any more.
Any thoughts are more than welcomed.
For DOM elments added dynimically you need to use the jquery .live() function.
Please go through the below link, I think that might fix your problem:
api.jquery.com/live
#ishwebdev, this is common problem we run , for all the DOM elments added after pageload like run time, we need to bind the events through live instead of regular bind
If you are using jquery 1.4 use below code:
// from jquery.com
$('give your selector here').live('mouseover mouseout', function(event) {
if (event.type == 'mouseover') {
// do something on mouseover
} else {
// do something on mouseout
}
});
#siri: thanks for the excellent answer, it worked for me right away. Here's my shopping cart dropdown example:
Before:
$("#cart-items").mouseenter(function(){
$('#cart-pulldown').show();
});
After:
$("#cart-items").live('mouseenter', function(){
$('#cart-pulldown').show();
});
With .live the event handling still works even after I change the underlying HTML via an Ajax call.
The selected answer no longer works for jquery 1.9+.
Instead, use "on" event, like
$( document ).on("keyup", "input.assets",function(event) {...
http://api.jquery.com/on/

Add click event to Div and go to first link found

I think I've been too much time looking at this function and just got stuck trying to figure out the nice clean way to do it.
It's a jQuery function that adds a click event to any div that has a click CSS class. When that div.click is clicked it redirects the user to the first link found in it.
function clickabledivs() {
$('.click').each(
function (intIndex) {
$(this).bind("click", function(){
window.location = $( "#"+$(this).attr('id')+" a:first-child" ).attr('href');
});
}
);
}
The code simply works although I'm pretty sure there is a fairly better way to accomplish it, specially the selector I am using: $( "#"+$(this).attr('id')+" a:first-child" ). Everything looks long and slow. Any ideas?
Please let me know if you need more details.
PS: I've found some really nice jQuery benchmarking reference from Project2k.de here:
http://blog.projekt2k.de/2010/01/benchmarking-jquery-1-4/
Depending on how many of these div.click elements you have, you may want to use event delegation to handle these clicks. This means using a single event handler for all divs that have the click class. Then, inside that event handler, your callback acts based on which div.click the event originated from. Like this:
$('#div-click-parent').click(function (event)
{
var $target = $(event.target); // the element that fired the original click event
if ($target.is('div.click'))
{
window.location.href = $target.find('a').attr('href');
}
});
Fewer event handlers means better scaling - more div.click elements won't slow down your event handling.
optimized delegation with jQuery 1.7+
$('#div-click-parent').on('click', 'div.click', function () {
window.location.href = $(this).find('a').attr('href');
});
Instead of binding all the clicks on load, why not bind them on click? Should be much more optimal.
$(document).ready(function() {
$('.click').click(function() {
window.location = $(this).children('a:first').attr('href');
return false;
});
});
I would probably do something like;
$('.click').click(function(e){
window.location.href = $(this).find('a').attr('href');
});

Categories