-1

On my web page I have panels which contain various input widgets, some of which are Bootstrap button groups. The basic HTML for these looks similar to this:

<div class="btn-group btn-group-justified param-widget param-widget-buttongroup">
    <div class="btn-group" role="group">
        <button type="button" class="btn">Option A</button>
    </div>
    <div class="btn-group" role="group">
        <button type="button" class="btn">Option B</button>
    </div>
    <div class="btn-group" role="group">
        <button type="button" class="btn">Option C</button>
    </div>
</div>

The panels of inputs are generated via Knockout templates, and are shown or hidden depending on which menu item the user selects in a main menu tree. This is done using jQuery's addClass() and removeClass() to add or remove the hide class that's present in Bootstrap/AdminLTE. In both cases this class is just defined as display: none !important.

I have jQuery code that performs tasks when the user provides input. For the buttons, it looks like this:

$(".param-widget-buttongroup .btn-group button").on("click", function()
{
    // Do some stuff here - send messages to the device I'm controlling, etc.
});

This is done after the Knockout data bindings are applied. It definitely used to work, and still does for my other non-buttongroup inputs - however, at some point in the last month or so of development it's stopped working, and I'm currently trying to work out why.

If I place a console.log() in the click handler function, nothing is printed when the button is clicked. However, reporting $(".param-widget-buttongroup .btn-group button").length at the point when I'm attempting to add the click handler returns a count of 152 (ie. all the buttons across all the panels), so the buttons are definitely being found.

I've eventually determined that if I execute $(".param-widget-buttongroup .btn-group button").on("click", function(){ console.log("Button pressed"); }) in the Chrome web console, I only actually see this on click if the button's panel is visible at the time I attach the handler. Buttons whose panels are not visible do not get that handler set on them, even though the number of buttons found by the selector is still reported as 152. Once the handler is attached, however, I can show a different panel and come back to the original one later, and the expected output will still be printed on click.

Is there any reason jQuery would decide not to attach the click handler if the item is hidden at the time? I've not had this issue with any of the other inputs I'm using (single buttons, text boxes, select boxes, ion range sliders, ...).

Alternatively, is there a way I can definitively check what click handler an element currently has? I've tried checking the $(...)[0].onclick value of the element, but regardless of whether the jQuery handler was attached successfully or not, this value is always null.

NoodleCollie
  • 855
  • 7
  • 24
  • Use event delegation i.e. `$(document).on("click", ".param-widget-buttongroup .btn-group button", function() { // Do some stuff here - send messages to the device I'm controlling, etc. });` I think element are not added to DOM when hidden – Satpal Jul 18 '18 at 08:13
  • being hidden shouldnt affect event attachment, are you sure they are hidden? meaning are they possibly not added to the document yet – Patrick Evans Jul 18 '18 at 08:13
  • The elements are definitely added to the document. This is done using Knockout templates (I'll add that to the question), but even if the handler addition was attempted too early, my manual execution through the Chrome web console should definitely be late enough. – NoodleCollie Jul 18 '18 at 08:27
  • @Satpal - you're right! Could you give a bit more information as to why that call works? (Ideally in the form of an answer, so I can accept it as the solution) – NoodleCollie Jul 18 '18 at 08:32
  • it should work. `display:none` elements are still part of the DOM. They are not removed. CSS cannot manipulate the DOM. See here example [jsfiddle](https://jsfiddle.net/39bv0uoe/26/) ( after 1 sec in console ) – Mihai T Jul 18 '18 at 08:36
  • Learn [Event Delegation](http://learn.jquery.com/events/event-delegation/) – Satpal Jul 18 '18 at 08:38
  • Looks like this question's considered a duplicate then. However, I'm still not 100% on why the jQuery selector would find elements if they're not considered in the DOM - or in other words, why elements can be "found" but not manipulated. – NoodleCollie Jul 18 '18 at 08:55

1 Answers1

-1

Your JS code is set to select CSS classes.

Actually, the button itself hasn't a class "button" but "btn" one.

Have you tried the following?

 $(".param-widget-buttongroup .btn-group btn").on("click", function()
{
    // Do some stuff here - send messages to the device I'm controlling, etc.
});
Istorn
  • 485
  • 1
  • 5
  • 23
  • OP selects the `button` inside elements with class `.btn-group` which is correct. What you are doing is selecting element with name `btn` which does not exist inside a class `.btn-group`. If you wanted to select the class `btn` you should use the `.` selector -> `.btn` – Mihai T Jul 18 '18 at 08:25
  • Should that not be `.btn`? The last part of the selector currently just uses `button` as the element type rather than `.btn` as the class, but I wouldn't expect that to make a difference seeing as the selector already finds all my buttons. I'll give it a try to check, though. EDIT: Ninja'd – NoodleCollie Jul 18 '18 at 08:25