Solution for on change doesn't work when input is changed programmatically

Viewed 20419

I have already read Why onchange doesn't work? and now I know that,

The onchange event only fires if the user changes the value of the input. It isn't supposed to fire if the input is changed programmatically.

But I have an input field which is automatically filled when user change a location by Google map widget inside a website. When it happens, I want to get that automatically filled value and fill another input filed. How can I detect or fire function when input is changed programmatically?

6 Answers

You could achieve this by overriding the setter function of the input's value property. So every time anyone sets the value programmatically your overriden setter will be triggered:

const input = document.getElementById('input');

const descriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(input), 'value');

Object.defineProperty(input, 'value', {
    set: function(t) {
        console.log('Input value was changed programmatically');
        return descriptor.set.apply(this, arguments);
    },
    get: function() {
      return descriptor.get.apply(this);
    }
});

function changeInput() {
  input.value += '1';
}
<input id="input">
<button onclick="changeInput()">Change</button>

For programmatically changing values in input tag, you may use triggers.

$(document).on('mytrigger keyup', '#input_num', function() {
  var value = $('#input_num').val();
  //alert(value);
  $('#result').val(value)
})

function external_modifier(){
  setTimeout(function(){
    var intValue = parseInt($('#input_num').val(), 10);
    $('#input_num').val(intValue+1).trigger("mytrigger");
    //alert(intValue);
    external_modifier();
  }, 3000);
}

external_modifier();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<lable> Input: </lable><input id='input_num' type=text value=1 />

<br><br>

<lable> Result: </lable><input id='result' type=text disabled />

As a solution for your problem when the user changes a location by Google map widget we will set the value programaticlly then we fire input change event manually by calling $("#field-id").trigger("change");.

For example, if your google maps code is like this. please check the code of function markerLocation

HTML:

<!DOCTYPE html>
<html>
    <head>
        <style type="text/css">
          #map{ width:700px; height: 500px; }
        </style>
        <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js"></script>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
        <title>Save Marker Example</title>
    </head>
    <body>
        <h1>Select a location!</h1>
        <p>Click on a location on the map to select it. Drag the marker to change location.</p>

        <!--map div-->
        <div id="map"></div>

        <!--our form-->
        <h2>Chosen Location</h2>
        <form method="post">
            <input type="text" id="lat" ><br>
            <input type="text" id="lng" >
        </form>

        <script type="text/javascript" src="map.js"></script>
    </body>
</html>

JS:

//map.js

//Set up some of our variables.
var map; //Will contain map object.
var marker = false; ////Has the user plotted their location marker? 

//Function called to initialize / create the map.
//This is called when the page has loaded.
function initMap() {

    //The center location of our map.
    var centerOfMap = new google.maps.LatLng(52.357971, -6.516758);

    //Map options.
    var options = {
      center: centerOfMap, //Set center.
      zoom: 7 //The zoom value.
    };

    //Create the map object.
    map = new google.maps.Map(document.getElementById('map'), options);

    //Listen for any clicks on the map.
    google.maps.event.addListener(map, 'click', function(event) {                
        //Get the location that the user clicked.
        var clickedLocation = event.latLng;
        //If the marker hasn't been added.
        if(marker === false){
            //Create the marker.
            marker = new google.maps.Marker({
                position: clickedLocation,
                map: map,
                draggable: true //make it draggable
            });
            //Listen for drag events!
            google.maps.event.addListener(marker, 'dragend', function(event){
                markerLocation();
            });
        } else{
            //Marker has already been added, so just change its location.
            marker.setPosition(clickedLocation);
        }
        //Get the marker's location.
        markerLocation();
    });
}

//This function will get the marker's current location and then add the lat/long
//values to our textfields so that we can save the location.
function markerLocation(){
    //Get location.
    var currentLocation = marker.getPosition();
    //Add lat and lng values to a field that we can save.
    $("#lat").value = currentLocation.lat(); //latitude
    //fire the change event of the input lat field
    $("#lat").trigger("change");

    $("#lng").value = currentLocation.lng(); //longitude
    //fire the change event of the input lng field
    $("#lng").trigger("change");
}


//Load the map when the page has finished loading.
google.maps.event.addDomListener(window, 'load', initMap);

To trigger an event programmatically, you can use the Event API : https://developer.mozilla.org/fr/docs/Web/API/EventTarget/dispatchEvent

Full example :

let input = document.getElementById('a')

// Listen the event
input.addEventListener('change', function (evt) {
   console.log('onchange event listener')
});

input.onchange = () => {
console.log('onchange callback')
}

setTimeout(() => {
input.value = "myvalue"

// Trigger the event
var event = new Event('change');
input.dispatchEvent(event);

}, 1000)
<input id="a">

I'm not sure if there's any event that fires if code changes an input's value. What you could do, though, is have an interval running that constantly compares the input's value to its previous value and does something if it's changed.

const inputBox = document.getElementById('my_input');
let previousValue = inputBox.value;
setInterval(() => {
  if (inputBox.value !== previousValue) {
    // fire change handler, then...
    previousValue = inputBox.value;
  }
}, 500);

Check this below code, this code checks for change in the value.

Just for better understanding two examples are taken, Both the cases are using same piece of code. It is just to illustrate the usage of code written.

var executeChange = false;
var lastValue = '';
var lastValOfManualBox = '';
$(document).ready(function() {
  setInterval(function() {
    if (!executeChange) {
      $('#boxWithGoogleMapValue').val("value1");
      executeChange = true;
    } else {
      $('#boxWithGoogleMapValue').val("value2");
      executeChange = false;
    }
  }, 5000);
  setInterval(function() {
    if ($("#boxWithGoogleMapValue").val() != lastValue) {
      lastValue = $("#boxWithGoogleMapValue").val();
      //console.log(lastValue);
      $("#boxToBeChanged").val(lastValue);
    }
    if ($("#manualBoxWithGoogleMapValue").val() != lastValOfManualBox) {
      lastValOfManualBox = $("#manualBoxWithGoogleMapValue").val();
      //console.log(lastValOfManualBox);
      $("#manualBoxToBeChanged").val(lastValOfManualBox);
    }
  }, 500);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>This boxes will be changing automatically every 5 seconds</p>
<input type="text" id="boxWithGoogleMapValue">
<br><br>
<input type="text" id="boxToBeChanged">

<p>The below boxes allow manual change(Enter values through keyboard)</p>
<input type="text" id="manualBoxWithGoogleMapValue">
<br><br>
<input type="text" id="manualBoxToBeChanged">

Hope this helps.

Related