Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

slideToggle executing twice in one call

Tags:

jquery

I can't show a live example at this point, but i don't see how the slideToggle is being called twice. it is called on click of a link.

here:

// initialize the jquery code
 $(function(){
  $('div.slideToggle a').click(function(){
   $(this).parent().siblings('div.remaining-comments').slideToggle('slow',function(){ //'this' becomes the remaining-comments.
    var hidden = $(this).siblings('div.slideToggle').find('input').val();
    if($(this).siblings('div.slideToggle').find('a').html().substr(0,4) == 'Show'){
     $(this).siblings('div.slideToggle').find('a').html('Hide comments');
    }
    else {
     $(this).siblings('div.slideToggle').find('a').html(hidden);
    }
   });
  });
 });

it is meant to display and hide extra comments on a blog page. however, it is displaying and then hiding in one click. I have put an alert in both the 'if' and 'else' and both appear, so how is it invoked twice?

obviously when the link contains the text 'Show' it reveals the hidden div, and when clicked again it will not find 'Show' and will therefore hide the div. The funny thing is, it was working absolutely perfectly. It's on the company intranet so i suppose maybe something else could be affecting it, but i really don't see how.

like image 603
Alex B Avatar asked Sep 30 '10 10:09

Alex B


2 Answers

It is possible that your document ready is called more than once, when page reinitializes, best thing to do is to unbind the click event before binding a new one to it.

this way you avoid having the click event bound twice on 1 element

try this:

$('div.slideToggle a').unbind('click').click(function(){
  // your code here
});

UPDATE:

The problem with this above situation is that you have to bind it again to every extra element that is added after you bound the click function the first time.

You can fix this issue, by binding 1 click handler on a parent item that stays in the dom. Giving it a sub-selector as argument.

It results in only 1 click handler being added, instead of 1 for each element. So you should win on performance, code execution time, and amount of javascript you were writing.

example:

let say you have a list where you dinamically add new elements

<ul id="ContactList">
    <li>John Doe <a href="#">Like</a></li>
    <li>Jane Doe <a href="#">Like</a></li>
    <li>Josh Doe <a href="#">Like</a></li>
    <li>Jeff Doe <a href="#">Like</a></li>
</ul>

and you bind some functionality on the anchor tag when it is clicked

$(function(){

    $('#ContactList').on('click', 'li a', function(e){
        // do your thing here...
        var el = $(e.currentTarget);
        el.addClass('iLikeYou');
    });

});

now, no matter how many items you add later on, it will work anyway. because it accepts clicks anywhere within the ul#ContactList and will execute the callback only when the clicked element matches the selector li a.

while $('#ContactList li a') would first get all the elements and then bind the click to all selected items. Newly added items afterwards don't have the click event and would need to have it bound to them first.

like image 70
Sander Avatar answered Oct 20 '22 15:10

Sander


Prologue

The answer to your questions points out something quite strange about jQuery. I ran into this problem when I tried to hide/slide divs and show/slide another one (in a jquery/Ajax navigation site), not knowing which divs were already visible.

Possible Answer

When a jquery calls more than one element in the same call, the function is executed more than once.

Exemple

$("#div_one, #div_two").hide("slow", function() {
   $("#div_two").show("slow");
});

--> #div_two will appear, disappear, and then appear again, because the chained function will trigger twice.

Additionnal info

Same thing happen with a call to a class with multiple elements.

Solution

In my case, I simply decided to do things differently. The first divs only disappear (no slide effect), and the new one can have a slide effect. This way, I don't need to wait for the first divs to slide before triggering the new div, so I don't have to encapsulate the second command into a chained function.

In this exemple, the alert only trigger once :

$("#div_one, #div_two").hide();
$("#div_two").show("slow");
});

It changes the user experience, but it works...

like image 21
MMB Avatar answered Oct 20 '22 16:10

MMB