2

I have this menu, statically, it is as follows:

<div class="dropdown col-sm-3">
        <button type="button" 
            class="btn btn-select btn-block"
            data-toggle="dropdown">Plane <span class="caret"></span>
        </button>
        <ul class="dropdown-menu dropdown-menu-select" id="plane">
            <li><label class="dropdown-radio"> <input type="radio" value="3" name="plane"> Plane: A360 </label></li>
            <li><label class="dropdown-radio"> <input type="radio" value="2" name="plane"> Plane: AR45 </label></li>
            <li><label class="dropdown-radio"> <input type="radio" value="1" name="plane"> Plane: A380 </label></li>
        </ul>
    </div>
...

<script>
    $('.dropdown-radio').find('input').change(function() {
      var dropdown = $(this).closest('.dropdown');
      var radioname = $(this).attr('name');
      var checked = 'input[name=' + radioname + ']:checked';

      //update the text
      var checkedtext = $(checked).closest('.dropdown-radio').text();
      dropdown.find('button').text( checkedtext );

      //retrieve the checked value, if needed in page 
      var thisvalue = dropdown.find( checked ).val();
      console.log( thisvalue );
    });
</script>

This version worked: the user could select one (and only one) option, and the option would then be displayed in the main button. I have now created this same menu dynamically with data from a MySQL database.

<script>
var socket = io.connect('http://localhost:3000');

socket.on('sess', function(message) {

if(message == 0){
    alert('No planes associated.');
}else{
    for(var i = 0; i < message.length; i++){
        $('#session').prepend( '<li><label class="dropdown-radio" > <input type="radio" value="' 
        + message[i].idSession + '" name="session"> Session: ' 
        + message[i].idSession + ' </label></li>');
    }
    }
});
</script>

Here the menu still drops down but now the user can check as many radios as they want and the first script isn't activated (nothing appears in the console). I have tried putting the second script both before and after the previous one, both give the same results.

Is there a workaround for this? I'm new to Javascript (jQuery) and its sensibilities.

Thanks

enapupe
  • 15,691
  • 3
  • 29
  • 45
Jessica Chambers
  • 1,246
  • 5
  • 28
  • 56
  • 2
    It is probably related to the fact that your script runs before the data from the database is retrieved, it should be run after adding the new radios, try wrapping the first script in a function and calling it at the end of the socket.on function – Silvio Biasiol Jan 29 '19 at 15:34
  • @SilvioBiasiol Oops I should have added that I've tried putting them in both orders and neither worked – Jessica Chambers Jan 29 '19 at 15:37
  • 1
    The first script runs once inline of the page load and due to the asynchronous call to your database, that means it doesn't find anything to bind `change` to and then the script is never run again. You'll need to update your script so that it is run again _after_ the new radio buttons have all been loaded into the DOM (this is after in _time_, not on the page). – PAB Jan 29 '19 at 15:38
  • @PhilB.That makes sense... How would you recommend I do that? – Jessica Chambers Jan 29 '19 at 15:40
  • 1
    Consider saving your first script to a function and calling it from your database call success, post rendering your radio buttons. You may need to use `.off('change')` also to remove any existing change bindings before creating new ones. – PAB Jan 29 '19 at 15:40
  • 1
    BTW, would be nice to mention `jquery` on a tag or on the description, it's not explicit anywhere. Somebody that doesn't know jquery's api would be unsure. – enapupe Jan 29 '19 at 16:04
  • 1
    Possible duplicate of [Event binding on dynamically created elements?](https://stackoverflow.com/questions/203198/event-binding-on-dynamically-created-elements) – Heretic Monkey Jan 29 '19 at 16:05

2 Answers2

1

I've combined my comments to an answer for easier reading/for anyone else looking at this.


The first script runs once inline of the page load and due to the asynchronous call to your database, that means it doesn't find anything to bind change to and then the script is never run again. You'll need to update your script so that it is run again after the new radio buttons have all been loaded into the DOM (this is after in time, not on the page).

Consider saving your first script to a function and calling it from your database call success, post rendering your radio buttons. You may need to use .off('change') also to remove any existing change bindings before creating new ones.

With regards to your radio buttons allowing multiple selected, as long as they share a name then they shouldn't allow multiple as per the documentation.

PAB
  • 165
  • 1
  • 9
1

What you want is to bind the change event to a higher on hierarchy element rather then directly at the inputs that didn't exist yet:

$('#plane').on('change', 'input', function() {
      var dropdown = $(this).closest('.dropdown');
      var radioname = $(this).attr('name');
      var checked = 'input[name=' + radioname + ']:checked';

      //update the text
      var checkedtext = $(checked).closest('.dropdown-radio').text();
      dropdown.find('button').text( checkedtext );

      //retrieve the checked value, if needed in page 
      var thisvalue = dropdown.find( checked ).val();
      console.log( thisvalue );
    });

Doing so, you are binding the change event to the parent div, so doesn't matter how you modify its children on a later moment, it will always fire an event for it if the target element was an input (second function argument).

You don't need to execute this script AFTER the inputs were loaded, just go with your initial approach on dynamically loading content but tweak that change with the on('change' version.

enapupe
  • 15,691
  • 3
  • 29
  • 45