javascriptjqueryselect

How to 'observe' when a select option is programmatically set


I'm sure this is easy, but I can't find it.

I want to know when a select is changed (new option selected) through code. Not options added or removed, but just what would be considered a simple onchange of the select. But the 'change' is through javascript.

Here is a snippet:

// find elements
var banner = $("#banner-message");
var button = $("button");
var sel = $('#myselect');

let observer = new MutationObserver(function(mutations) {
  alert('in observer');
});

observer.observe(sel[0], { attributes: true, subtree: true });

let counter = 2;
button.on("click", () => {
  if (counter == 1) {
    sel.val('one');
    counter = 2;
  } else {
    sel.val('two');
    counter = 1;
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<div id="banner-message">
  <select id='myselect'>
    <option>one</option>
    <option>two</option>
  </select>
  <button>test</button>
</div>

I cannot get the observer code to fire.


Solution

  • You can monkeypatch HTMLOptionElement#selected's setter:

    // find elements
    var banner = $("#banner-message");
    var button = $("button");
    var sel = $('#myselect').on('change', e => {
      console.log(sel.val());
    });
    
    const {get, set} = Object.getOwnPropertyDescriptor(HTMLOptionElement.prototype, 'selected');
    Object.defineProperty(HTMLOptionElement.prototype, 'selected', {
      get,
      set(val){
        console.log('select value:', this.value, ' of ', this.parentElement.id, ':', val);
        set.call(this, val);
        $(this.parentElement).change();
      }
    });
    
    let counter = 2;
    button.on("click", () => {
      if (counter == 1) {
        sel.val('one');
        counter = 2;
      } else {
        sel.val('two');
        counter = 1;
      }
    })
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
    <div id="banner-message">
      <select id='myselect'>
        <option>one</option>
        <option>two</option>
      </select>
      <button>test</button>
    </div>