Determine if Javascript select 'change' event performed by user versus via code?

Viewed 580

Is there a way to determine if a select element's change event was performed by user versus via code (dom manipulation)?

I have a timer that auto sets some select values based on some condition and then formats the select color to signify "success" (green select box).

Because I have the form set to fire off an ajax request when the select change event is fired, it does this for both the timer-based changes to the selects, as well as user-based select changes.

I'd like to only fire that ajax event when it's a user-based select change.

Is this possible?

EDIT:

When I output the value of e.isTrusted, it says Undefined in the log.

Here is my code:

    $(document).on('change', 'select', function (e) {
            e.preventDefault();

            console.log(e.isTrusted); // This outputs "Undefined"

            if (e.isTrusted) {
                save_review();
                console.log("Point status changed by user - Saved");
            } else {
                console.log("Point status changed programmatically - Not Saved");
            }
        });
3 Answers

Managed to solve this based on a solution I found googling and with the help of the community-provided answers above.

When the timer-based event triggers, I now pass it a boolean value (auto_selected):

$("#pt" + value + " option:nth-child(2)").attr('selected', true).trigger('change', [true]);

In my event listener, I am then able to check against the value of that parameter:

        $(document).on('change', 'select', function (e, auto_selected) {
            e.preventDefault();

            console.log("Auto-selected: " + auto_selected);

            if (typeof auto_selected == 'undefined' || !auto_selected) {
                save_review();
                console.log("Point status changed by user - Saved");
            } else {
                console.log("Point status changed programmatically - Not Saved");
            }
        });

There is a property called isTrusted on the event object that you can use to check whether the event is triggered by user action or by script.

that is true when the event was generated by a user action, and false when the event was created or modified by a script or dispatched via EventTarget.dispatchEvent()

if (e.isTrusted) {
  /* The event is trusted */
} else {
  /* The event is not trusted */
}

if you use jQuery, it's inside event.originalEvent

if (e.originalEvent && e.originalEvent.isTrusted) {
  /* The event is trusted */
} else {
  /* The event is not trusted */
}

If you use select2, listen to the event select2:select, and check for event.params.originalEvent.originalEvent (two originalEvent here, seems like a bad implementation inside the library itself)

$('#your-selector').on('select2:select', function(e) {
   if(e?.params?.originalEvent?.originalEvent) {
     // user action

   } else {
     // programatically trigger
   }

});

I looked a bit through the documentation of select2 because I was not really happy with the initial solution in my answer. And they actually provide a solution for that using a namespaced events.

Limiting the scope of the change event:

It's common for other components to be listening to the change event, or for custom event handlers to be attached that may have side effects. To limit the scope to only notify Select2 of the change, use the .select2 event namespace

I'm wondering why they don't suggest that one as default in the documentation. I still think that their suggested way (using the not namespace verion) to sync the native select element with the HTML representation of select2 is odd. Because why should the default behavior of programmatically changing an input element trigger a change event.

So if you use $("select option:nth-child(2)").attr('selected', true).trigger('change.select2'); it won't trigger the change event.

$('select').select2();


function commonTaskOnChange() {
   console.log('commonTask')
}

$(document).on('change', 'select', function (e) {
   console.log('selection changed by user')
   
   // in case you want to perform some common task for timer and change event
   commonTaskOnChange();
})

setTimeout(() => {
   console.log('timer will change selection')

   // trigger the namespaced change event
   $("select option:nth-child(2)").attr('selected', true).trigger('change.select2');

   // you could still perform some action that the change event has in common by calling a function here
   commonTaskOnChange();
}, 1000)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>

<select>
  <option>1</option>
  <option>2</option>
  <option>3</option>
</select>

Related