0

I have a CodePen that loads cities after a country is selected..., now with //$("#country").val("3"); code commented the "manual" change of the coutry works well.

But the "automatic" (code uncommented) load of the country does not change the cities... why?

PS. I don't want to trigger "change" event manually. for me is strange I should do it by hand...

document.addEventListener("DOMContentLoaded", function(event) {

var countries = {
  "1": ["Paris", "Lyon"],
  "2": ["Berlin", "Cologne"],
  "3": ["London", "Edinburgh"]
};

function loadCities(countryId) {
  var cities = countries[countryId];
  var container = "#city";
  $(container).empty();
  $(container).append($("<option/>", { value: 0, text: "select a city..." }));
  $.each(cities, function(key, city) {
    $(container).append($("<option/>", { text: city }));
  });
}

$("#country").on("change", function() {
  var countryId = $(this).val();
  loadCities(countryId);
});

$("#country").val("3");//.trigger("change");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<select id="country">
  <option value="0">select a country</option>
  <option value="1">France</option>
  <option value="2">Germany</option>
  <option value="3">UK</option>
</select>
<select id="city">
  <option value="0">no country selected</option>
</select>
serge
  • 13,940
  • 35
  • 121
  • 205
  • you have a weird mix of js and jQuery going on - what happens when you change the first line to: `jQuery(document).ready(function($) {` – treyBake Oct 26 '18 at 15:21
  • 1
    Possible duplicate of [val() doesn't trigger change() in jQuery](https://stackoverflow.com/questions/3179385/val-doesnt-trigger-change-in-jquery) – Peter B Oct 26 '18 at 15:23
  • If you run your snippet, there is an error of `"Uncaught ReferenceError: $ is not defined"`. If you change your snippet to a newer version of jQuery such as 2.x.x, your code works. Try changing the version of jQuery in your snippet. – Alexander Staroselsky Oct 26 '18 at 15:23
  • @PeterB: I don't want to manually trigger "change". – serge Oct 26 '18 at 15:24
  • 1
    @Serge You have to. Logical changes do not create events. Events are for user interactions. This is explained in the duplicate – Taplar Oct 26 '18 at 15:24
  • @AlexanderStaroselsky: please see the Codepen, it strange why it does not work, jquery not loading... – serge Oct 26 '18 at 15:25
  • @Serge my first comment on this thread will fix that `ReferenceError` issue – treyBake Oct 26 '18 at 15:30
  • User @Sv443 made a stupid edit that broke the spelling of Edinburgh + which removed the jQuery library. It is too late to do a rollback of that because you have made an edit on top of it already (which would then also be undone)... – Peter B Oct 26 '18 at 15:32
  • @PeterB updated the snippet – serge Oct 26 '18 at 15:34
  • @ThisGuyHasTwoThumbs fixed by applying the Alexander suggestion – serge Oct 26 '18 at 15:35
  • I didn't break anything. I just removed the jQuery script tag because it's automatically included – Sv443 Oct 26 '18 at 15:35
  • @Sv443 what is your problem with "Edinburgh"? – serge Oct 26 '18 at 15:37
  • Side point. The `$.each` is unnecessary. The key of your cities is your countryId. You can do just `cities[countryId]` – Taplar Oct 26 '18 at 15:38
  • @Serge I didn't change a single thing. Like I said, I only made a single edit and that was to remove that first line in the HTML section and I changed the jQuery version in the snippet's settings. I didn't even click on anything else. – Sv443 Oct 26 '18 at 15:39
  • 1
    Well I just looked at the revisions and you're right I did somehow change that. I don't know how that happened but alright. – Sv443 Oct 26 '18 at 15:47
  • @Taplar for your "cities[countryId]" remark the "==" sign, that mean, if the keys are integers we sould compare them by value, not by type... – serge Oct 26 '18 at 15:51
  • The only reason you are doing that is because you are looping through all the entries in the object and trying to find the one that has the cities for the countryId. But it's an object. You do not have to do that. The countryId is the key of the object. I made use of this fact in my answer below – Taplar Oct 26 '18 at 15:53
  • @Taplar i doing this because in my real example I recieve integers from json as ID, but have strings as key, so "2" and 2 is not the same, I should compare "==", not "===" – serge Oct 26 '18 at 16:00
  • 1
    then `countryId.toString()` will fix that. There's no need for the loop – Taplar Oct 26 '18 at 16:00
  • @Taplar good point... – serge Oct 26 '18 at 16:00

3 Answers3

3

If you don't want to "trigger change" you can always call the function on your own.

function selectCountry(countryId) {
    $("#country").val(countryId);
    loadCities(countryId)
}

then replace this:

$("#country").val("3");//.trigger("change");

with this:

selectCountry("3")
dovidweisz
  • 1,215
  • 13
  • 24
  • 2
    This is actually more preferable, as you are not funnling your logic through the dom. You're going straight to what you want to do. – Taplar Oct 26 '18 at 15:32
  • @dovidweisz just please show how to include it in the actual code ;) – serge Oct 26 '18 at 16:54
2

You have to also trigger the change event, like this:

$("#country").val("3");
$("#country").trigger("change");

onchange only fires when the user types into the input and then the input loses focus. It cannot be the default action as many times you do not want the event triggering from an automated value change, only when a user interacts with the element.

The reason may be that some people call .val() in .change() because they do input validation. Triggering .change() on .val() would cause an infinite loop.

Tudor Constantin
  • 26,330
  • 7
  • 49
  • 72
1

document.addEventListener("DOMContentLoaded", function(event) {
  var cities = {
    "1": ["Paris", "Lyon"],
    "2": ["Berlin", "Cologne"],
    "3": ["London", "Edinburgh"]
  };

  function loadCities(countryId) {
    //get the city names if we can
    var cityNames = cities[countryId];
    //get a reference to the container
    var $container = $('#city');
    //create an array of the options we want to replace the city with
    //building a list of the options allows us to optimize in two ways
    //1) Not using jQuery to create the options removes some overhead with
    //   jQuery creating the DOM nodes one at a time
    //2) Replacing all the options at the end reduces the number of times
    //   you manipulate the DOM and reduce the number of times it has to
    //   perform calculations about how it is going to re-draw the page
    var options = ['<option value="0">select a city...</option>'];
    
    //we will have cityNames if the countryId is not 0
    if (cityNames) {
      //make an option for each city
      cityNames.forEach(function(city){
        options.push('<option>'+ city +'</option>');
      });
    } else {
      //user did not select a country, default the city back to the original
      //message
      options = ['<option value="0">no country selected</option>'];
    }
    
    //replace all the options with the new options.  This will be a single
    //DOM change
    $container.html(options);
  }

  $("#country")
    //bind the on change like we were before
    .on("change", function() {
      var countryId = $(this).val();
      loadCities(countryId);
    })
    //also change the value
    .val("3");
  //call loadCities directly to update the city dropdown
  loadCities("3");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<select id="country">
  <option value="0">select a country</option>
  <option value="1">France</option>
  <option value="2">Germany</option>
  <option value="3">UK</option>
</select>
<select id="city">
  <option value="0">no country selected</option>
</select>
Taplar
  • 24,788
  • 4
  • 22
  • 35
  • good, but I want my "val("3")" updates automatically the values if possible.. there should not be the need to call "loadCities("3");" – serge Oct 26 '18 at 15:55
  • This has already been explained with the fact that logical changes do not create events. – Taplar Oct 26 '18 at 15:56
  • my logical change updates the selected value and the text of the select element. this is an event for me... – serge Oct 26 '18 at 15:57
  • It's not an event for javascript. You have to work with the language. – Taplar Oct 26 '18 at 15:58