MatthewSot MatthewSot - 6 months ago 13
HTML Question

How to synchronize two SELECT elements

I was wondering how to synchronize the values and text of two elements. For instance,

<select id="box1" onchange="sync();">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
<select id="box2">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>


and then sync(); would look something like....

function sync()
{
box2.selected = box1.selected;
}


Any idea how I would do this?
Thanks,
Matthew

Answer

One possible approach:

function sync(el1, el2) {
    // if there is no el1 argument we quit here:
    if (!el1) {
        return false;
    }
    else {
        // caching the value of el1:
        var val = el1.value;

        // caching a reference to the element with
        // with which we should be synchronising values:
        var syncWith = document.getElementById(el2);

        // caching the <option> elements of that <select>:
        var options = syncWith.getElementsByTagName('option');

        // iterating over those <option> elements:
        for (var i = 0, len = options.length; i < len; i++) {

            // if the value of the current <option> is equal
            // to the value of the changed <select> element's
            // selected value:
            if (options[i].value == val) {

                // we set the current <option> as
                // as selected:
                options[i].selected = true;
            }
        }
    }
}

// caching the <select> element whose change event should
// be reacted-to:
var selectToSync = document.getElementById('box1');

// binding the onchange event using an anonymous function:
selectToSync.onchange = function(){

    // calling the function:
    sync(this,'box2');
};

function sync(el1, el2) {
  if (!el1) {
    return false;
  } else {
    var val = el1.value;
    var syncWith = document.getElementById(el2);
    var options = syncWith.getElementsByTagName('option');
    for (var i = 0, len = options.length; i < len; i++) {
      if (options[i].value == val) {
        options[i].selected = true;
      }
    }
  }
}

var selectToSync = document.getElementById('box1');
selectToSync.onchange = function() {
  sync(this, 'box2');
};
<select id="box1">
  <option value="1">One</option>
  <option value="2">Two</option>
  <option value="3">Three</option>
</select>
<select id="box2">
  <option value="1">One</option>
  <option value="2">Two</option>
  <option value="3">Three</option>
</select>

JS Fiddle demo.

Or, revised and updated somewhat:

function sync() {

  // caching the changed element:
  let el = this;

  // retrieving the id of the element we should synchronise with
  // from the changed-element's data-syncwith custom attribute,
  // using document.getElementById() to retrieve that that element.
  document.getElementById( el.dataset.syncwith )
    // retrieving the <options of that element
    // and finding the <option> at the same index
    // as changed-element's selectedIndex (the index
    // of the selected <option> amongst the options
    // collection) and setting that <option> element's
    // selected property to true:
    .options[ el.selectedIndex ].selected = true;
}

// retrieving the element whose changes should be
// synchronised with another element:
var selectToSync = document.getElementById('box1');

// binding the snyc() function as the change event-handler:
selectToSync.addEventListener('change', sync);

function sync() {
  let el = this;
  document.getElementById(el.dataset.syncwith).options[el.selectedIndex].selected = true;
}

var selectToSync = document.getElementById('box1');
selectToSync.addEventListener('change', sync);
<select id="box1" data-syncwith="box2">
  <option value="1">One</option>
  <option value="2">Two</option>
  <option value="3">Three</option>
</select>
<select id="box2">
  <option value="1">One</option>
  <option value="2">Two</option>
  <option value="3">Three</option>
</select>

JS Fiddle demo.

Note that this approach does assume – and requires – that the <option> elements are in the same order.

To update the original approach, where the order is irrelevant, using ES6 approaches (and the same data-syncwith custom attribute approach):

function sync() {
  // caching the changed element (since
  // we're using it twice):
  let el = this;

  // retrieving the id of the element to synchronise 'to' from 
  // the 'data-syncwith' custom attribute of the changed element,
  // and retrieving its <option> elements. Converting that
  // Array-like collection into an Array using Array.from():
  Array.from(document.getElementById(el.dataset.syncwith).options)
    // Iterating over the array of options using
    // Array.prototype.forEach(), and using an Arrow function to
    // pass the current <otpion> (as 'opt') setting that current
    // <option> element's selected property according to Boolean
    // returned by assessing whether the current option's value
    // is (exactly) equal to the value of the changed element:
    .forEach(opt => opt.selected = opt.value === el.value);
}

var selectToSync = document.getElementById('box1');
selectToSync.addEventListener('change', sync);

function sync() {
  let el = this;
  Array.from(document.getElementById(el.dataset.syncwith).options).forEach(opt => opt.selected = opt.value === el.value);
}

let selectToSync = document.getElementById('box1');
selectToSync.addEventListener('change', sync);
<select id="box1" data-syncwith="box2">
  <option value="1">One</option>
  <option value="2">Two</option>
  <option value="3">Three</option>
</select>
<select id="box2">
  <option value="1">One</option>
  <option value="3">Three</option>
  <option value="2">Two</option>
</select>

JS Fiddle demo.

If you look at the HTML in the Snippet you'll see that I switched the positions of <option> elements in the second <select> element to demonstrate that the <option> position doesn't matter in this latter approach.

References: