Search code examples
javascriptjqueryjquery-uijquery-ui-selectmenu

JQuery UI Selectmenu change fires on click


I have a selectmenu with custom rendering. Subsequent clicks will change the selected value to the first option (the unwanted behavior). In the code snippet, the initial selected value is "2", which is the 5th option. Click and select another option (not the first one), so far so good.

Click the control again and it will change to the first option firing the change event. It will do this for all subsequent clicks.

$.widget(".addressmenu", $.ui.selectmenu, {
  _renderItem: function(ul, item) {
    var li = $("<li>");

    $("<span>", {
        text: item.label
      })
      .addClass("ui-selectmenu-item-header addressmenu")
      .appendTo(li);

    if (item.element.attr("data-address1") != '') {
      $("<span>", {
          text: item.element.attr("data-address1")
        })
        .addClass("ui-selectmenu-item-content addressmenu")
        .appendTo(li);
    }

    if (item.element.attr("data-address2") != '') {
      $("<span>", {
          text: item.element.attr("data-address2")
        })
        .addClass("ui-selectmenu-item-content addressmenu")
        .appendTo(li);
    }

    if (item.element.attr("data-address3") != '') {
      $("<span>", {
          text: item.element.attr("data-address3")
        })
        .addClass("ui-selectmenu-item-content addressmenu")
        .appendTo(li);
    }

    if (item.element.attr("data-address4") != '') {
      $("<span>", {
          text: item.element.attr("data-address4")
        })
        .addClass("ui-selectmenu-item-content addressmenu")
        .appendTo(li);
    }

    return li.appendTo(ul);
  }
});

$('#ShipToCode').addressmenu({
  change: function() {
    alert("CHANGED!!!");
  }
}).addressmenu("menuWidget");
#checkout .ui-selectmenu-button {
  text-align: left;
}
.ui-selectmenu-item-header,
.ui-selectmenu-item-content {
  display: block;
}
.ui-menu .ui-menu-item-wrapper.addressmenu {
  padding: 2px 0 1px .5em;
}
.ui-state-active.addressmenu,
.ui-widget-content .ui-state-active.addressmenu {
  border: none;
  color: #00f;
  background: #f0f0f0;
}
.ui-selectmenu-item-header.addressmenu.ui-menu-item-wrapper.ui-state-active {
  font-weight: bold;
}
<link href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" rel="stylesheet"/>
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.0/jquery-ui.min.js"></script>

<select name="ShipToCode" id="ShipToCode" value="2">
  <option value="-2">Same as Billing</option>
  <option value="-1">Add new address</option>
  <option value="0001" data-address1="3121 W. 24th Street" data-address2="Suite 1233" data-address4="   USA">ABF Distribution Warehouse</option>
  <option value="1" data-address1="3121 W. 24th Street" data-address2="Suite 1233" data-address4="   USA">American Business Futures Dist</option>
  <option value="2" selected="selected" data-address1="5411 Kendrick Place" data-address4="   USA">Racine Warehouse</option>
  <option value="3" data-address1="5411 Kendrick Place" data-address4="   USA">ABF - Racine Warehouse</option>
</select>

I suspect this may have something to do with the custom rendering, but I can't see anything that I'm doing that might cause the issue.


Solution

  • It appears you were following this example. It was the lack of wrapper in your code that was creating the issue.

    When rendering, it was applying the wrapper to each span in your li element. So they would render like:

    <li class="ui-menu-item">
      <span class="ui-selectmenu-item-header addressmenu ui-menu-item-wrapper ui-state-active" id="ui-id-3" tabindex="-1" role="option">ABF Distribution Warehouse</span>
      <span class="ui-selectmenu-item-content addressmenu ui-menu-item-wrapper ui-state-active" id="ui-id-4" tabindex="-1" role="option">3121 W. 24th Street</span>
      <span class="ui-selectmenu-item-content addressmenu ui-menu-item-wrapper ui-state-active" id="ui-id-5" tabindex="-1" role="option">Suite 1233</span>
      <span class="ui-selectmenu-item-content addressmenu ui-menu-item-wrapper ui-state-active" id="ui-id-6" tabindex="-1" role="option">USA</span>
    </li>
    

    You can see how it has appended the ui-menu-item-wrapper class to each.

    Here is what I would advise, working example: https://jsfiddle.net/Twisty/gxx9agyj/4/

    HTML

    <select name="ShipToCode" id="ShipToCode" value="2">
      <option value="-2" data-type="same">Same as Billing</option>
      <option value="-1" data-type="new">Add new address</option>
      <option value="0001" data-type="address" data-address="3121 W. 24th Street|Suite 1233|USA">ABF Distribution Warehouse</option>
      <option value="1" data-type="address" data-address="3121 W. 24th Street|Suite 1233|USA">American Business Futures Dist</option>
      <option value="2" selected="selected" data-type="address" data-address="5411 Kendrick Place|USA">Racine Warehouse</option>
      <option value="3" data-type="address" data-address="5411 Kendrick Place|USA">ABF - Racine Warehouse</option>
    </select>
    

    I adjusted the data attributes a bit to make them more portable.

    jQuery

    $(function() {
      $.widget("custom.addressmenu", $.ui.selectmenu, {
        _renderItem: function(ul, item) {
          var li = $("<li>")
          var address;
          var wrapper = $("<div>", {
              class: "ui-selectmenu-item-header addressmenu",
              text: item.label
            })
            .appendTo(li);
    
          if (item.element.data("type") == "address") {
            address = item.element.data("address").split("|");
            $.each(address, function(k, v) {
              if (v != '') {
                var adLine = $("<span>", {
                  class: "ui-selectmenu-item-content addressmenu",
                  "data-address-line": k,
                  text: v
                });
                wrapper.append(adLine);
              }
            });
          }
    
          return li.appendTo(ul);
        }
      });
    
      $('#ShipToCode').addressmenu({
        select: function(e, ui) {
          console.log("Selected: " + ui.item.label);
        }
      }).addressmenu("menuWidget");
    });
    

    If the option is an address type, we do a little extra work. We split the address up and iterate over each line, making a span for each.

    Now when you select one, select another, it retains the proper previous selection.