1

First part of code works perfect, including: mouseover, mouseout and click event on id, called #active-to-do-list-28. But when click event changes attribute value, it doesn't react to id, called #inactive-to-do-list-28.

Code:

<script>
$(document).ready(function(){
  $("#active-to-do-list-28").mouseover(function(){
    $("#active-to-do-number-28").attr("class", "label label-info");
  });
  $("#active-to-do-list-28").mouseout(function(){
    $("#active-to-do-number-28").attr("class", "label label-default");
  });
  $("#active-to-do-list-28").click(function(){
    $("#active-to-do-list-28").attr("class","list-group-item list-group-item-info");
    $("#active-to-do-list-28").attr("id", "inactive-to-do-list-28");
    $("#active-to-do-number-28").attr("class", "label label-info");
    $("#active-to-do-number-28").attr("id", "inactive-to-do-number-28");
  });
  $("#inactive-to-do-list-28").click(function(){
    $("#inactive-to-do-list-28").attr("class", "list-group-item");
    $("#inactive-to-do-list-28").attr("id", "active-to-do-list-28");
    $("#inactive-to-do-number-28").attr("class", "label label-default");
    $("#inactive-to-do-number-28").attr("id", "active-to-do-number-28");
  });
});
</script>

Could anyone help me fix this problem?

Siguza
  • 21,155
  • 6
  • 52
  • 89
Paulius Vitkus
  • 129
  • 1
  • 4
  • 15
  • Since you are changing the ID the element is viewed as a newly created one and goes into the dynamically created elements (at the time when your code runs it can't find the element to bind to). You need to change your on() structure. See http://stackoverflow.com/a/1207393/2220391 – Spokey Oct 02 '14 at 15:04

4 Answers4

2

As you are changing the id, the event handlers for the new ID will not fire (as they did not exist at the time you tried to register the events on them). If you must use this style of code, use delegated event handlers:

e.g. like:

  $(document).on('mouseover', "#active-to-do-list-28", function(){
    $("#active-to-do-number-28").attr("class", "label label-info");
  });

of course this can be shortened as the second selector is the same element:

  $(document).on('mouseover', "#active-to-do-list-28", function(){
    $(this).attr("class", "label label-info");
  });

It works by listening for the specified events to bubble up to a non-changing ancestor. If nothing is closer, then document is the default. It then applies the jQuery selector at event time, so it does not matter if the object did not exist when the event was registered.

I would suggest you never change IDs, but have other classes instead.

Please note that most of your code can be greatly simplified, but it would help if you provided an example of your HTML too.

Updates:

  • There is an inherent problem changing the target of an element in a mouseenter event. in some browsers it may, or may not, produce a mouseleave event immediately as you are effectively pulling the carpet out from under the cursor!

  • The entire problem seems to be one of displaying uniquely on hover, and toggling selection on click. See below:

JSFiddle: http://jsfiddle.net/TrueBlueAussie/86pv4j51/

If so, a class-based solution would do something like this:

$(document).ready(function () {
    $('.list-group').on('mouseenter', '.list-group-item', function () {
        $(this).addClass("hover");
    }).on('mouseleave', '.list-group-item', function () {
        $(this).removeClass("hover");
    }).on('click', '.list-group-item', function(){
        $(this).toggleClass("active");
    });
});

Note: I left them as delegated events, but if the items are non-dynamic you could use normal event handlers.

iCollect.it Ltd
  • 92,391
  • 25
  • 181
  • 202
  • Thanks, @TrueBlueAussie. Simplifying and optimization of code is task no. 2 in my list. But you're right. It's not effective to create events for every ID separately. I will write HTML code in a few minutes. – Paulius Vitkus Oct 02 '14 at 15:14
  • Really neat solution, altho like you said, changing the IDs is a bad idea. What happened to using the data-x attribute, I hardly ever see people use that, even tho is widely supported already. – Victor D. Oct 02 '14 at 15:47
  • @Victor D.: I tend to use `data-` attributes for information all the time and leave classes for styling/selection. – iCollect.it Ltd Oct 02 '14 at 15:53
0

jquery .click event are made on page load so all id with #inactive-to-do-list-28 will be affected but if you add element with this id or change element with this id it will not have the event if you want you should do your event like so :

$(document).on('click','#inactive-to-do-list-28', function(){});

note : document isn't the best selector you should use a closer element. More info on the jquery .on() here: http://api.jquery.com/on/

Sebastien B.
  • 476
  • 3
  • 10
  • Never use 'body' for delegated events. It can be styled out (e.g. if the contents are floated) and not fire the events. Use `document` as the default fallback if nothing is closer. – iCollect.it Ltd Oct 02 '14 at 15:08
0

Really thanks to all you. It helped.

Now, as @TrueBlueAussie noticed, my code could be much more simple, but I don't know which optimization and simplyfing is best.

This is my HTML:

    <ul class="list-group" align="left"><li class="list-group-item" id="active-to-do-list-1" style="padding-bottom: 14px; line-height: 20px;"><span style="margin-right: 12px;"><span class="label label-default" id="active-to-do-number-1">1</span></span>Task1
                <div class="text-right" style="float: right;">
                    <a href="#deleteTodoDialog" class="open-DeleteTodo btn btn-danger" data-toggle="modal" data-id="1" style="height: 26px; padding-top: 4px; padding-bottom: 2px; margin-right: 5px; width: 40px;"><span class="glyphicon glyphicon-minus-sign"></span></a>
                    <a href="#editTodoDialog" class="open-EditTodo btn btn-warning" data-toggle="modal" data-id='{"id":1,"todo":"task1)"}' style="height: 26px; padding-top: 4px; padding-bottom: 2px; margin-right: 5px; width: 40px;"><span class="glyphicon glyphicon-edit"></span></a>
                    <a href="#doneTodoDialog" class="open-DoneTodo btn btn-success" data-toggle="modal" data-id="1" style="height: 26px; padding-top: 4px; padding-bottom: 2px; margin-right: 5px; width: 40px;"><span class="glyphicon glyphicon-ok-sign"></span></a>
                </div>
    </ul>

What you would recommend?

Paulius Vitkus
  • 129
  • 1
  • 4
  • 15
0

When you declare a jQuery event handler it will run through the page and look for all elements that match the given selector and attach the handler to it. In this case your code is in a document.ready block so it is run once on page load. If you change the ID or create new elements afterwards, none of these elements will be bound to the handlers that have already been declared.

The quick way to fix this would be to rebind the event handlers when change the ID. For example:

Put all of that code that is in the document.ready in a function, and then whenever you do something that changes the ID or class of an element, call this function again.

function eventsBinder(){
  $("#active-to-do-list-28").mouseover(function(){
    $("#active-to-do-number-28").attr("class", "label label-info");
  });
  $("#active-to-do-list-28").mouseout(function(){
    $("#active-to-do-number-28").attr("class", "label label-default");
  });
  $("#active-to-do-list-28").click(function(){
    $("#active-to-do-list-28").attr("class","list-group-item list-group-item-info");
    $("#active-to-do-list-28").attr("id", "inactive-to-do-list-28");
    $("#active-to-do-number-28").attr("class", "label label-info");
    $("#active-to-do-number-28").attr("id", "inactive-to-do-number-28");
    eventsBinder();
  });
  $("#inactive-to-do-list-28").click(function(){
    $("#inactive-to-do-list-28").attr("class", "list-group-item");
    $("#inactive-to-do-list-28").attr("id", "active-to-do-list-28");
    $("#inactive-to-do-number-28").attr("class", "label label-default");
    $("#inactive-to-do-number-28").attr("id", "active-to-do-number-28");
    eventsBinder();
 });
}

Then we call this function on the document ready event

$(document).ready(function(){
  eventsBinder();
})

That should do the trick. Ideally you'd want to remove previously bound events to those elements before attaching new ones to avoid them being called twice; but in this particular case it will not cause any trouble (other than maybe adding a few milliseconds of processing).

Hope it helps.

Victor D.
  • 141
  • 9
  • 1
    The chance of human error is very high with reconnecting events like that (you could easily wind up with multiple registrations). To be safe you would ensure they were turned `off` then `on`, but delegated events were designed to handle dynamic element changes. – iCollect.it Ltd Oct 02 '14 at 15:27
  • @TrueBlueAussie Indeed there is a high potential to end up with multiple handlers being fired by a single event. Which is why I sugested that the handlers were removed before adding new ones. I wanted to add as little code as possible to his already existing code, since multiple events fired will not disrupt the display here anyways. Maybe I shoud edit my answer and add it? – Victor D. Oct 02 '14 at 15:43
  • Ah yes, I see now that you did mention removing them. The problem with the example is that that comment would be easily overlooked (as I have accidentally proved). :) – iCollect.it Ltd Oct 02 '14 at 15:45
  • Can you explain, simply, what the overall aim is of the code? You seem to have about 10 times the code needed to do anything, but the aim escapes me (as I am missing the matching styling, can you add that too) :) – iCollect.it Ltd Oct 02 '14 at 15:49