18

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?

  • a quick solution would be to create a timer that checks for a change on the input and then trigger the function you want (or just trigger onChange) an example for such you can find here: https://stackoverflow.com/questions/3635924/how-can-i-make-a-program-wait-for-a-variable-change-in-javascript – Rafael Herscovici Oct 18 '19 at 12:02
  • 2
    There should be some API to detect location change event which you can use instead of putting your own change event handler. Check google map widget documentation – Bhushan Kawadkar Oct 18 '19 at 12:06
  • 1
    Trigger the event manually: [.trigger](https://api.jquery.com/trigger/#trigger-eventType-extraParameters) after you've changed the value. – Teemu Oct 18 '19 at 12:12
  • And your widget doesn't expose anything you can hook to in order to receive an event directly from there? Sounds very weird to me. When programmers make widgets, they generally also make an API so that this widget is somehow useful. – Kaiido Oct 21 '19 at 11:37
  • 1
    There was [this hack](https://stackoverflow.com/questions/55033836/how-do-you-listen-detect-changes-to-an-input-value-when-the-input-value-is-c/55033939#55033939), but really, just read again the manual of your widget to be sure you didn't miss something simple – Kaiido Oct 21 '19 at 11:47
  • How is the first input field filled? That should lead you to the correct answer. – Chava Geldzahler Oct 27 '19 at 22:10
  • Why not calling onchange wherever you change the input programmatically ? – nAviD Oct 28 '19 at 07:38
  • Marked as duplicate? The answer in above link ain't proper. – Jennis Vaishnav Nov 11 '19 at 06:28

6 Answers6

13

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>
Kirill Simonov
  • 8,257
  • 3
  • 18
  • 42
  • 2
    This is great, thanks! It's unfortunate that sometimes this is needed, this is the only solution when you need to act whenever a third party library or a framework is handling some JS and you need to react when the third party code changes the value. Thanks! – nemesisdesign Mar 16 '21 at 22:11
0

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 />
Rohit Lal
  • 2,791
  • 1
  • 20
  • 36
0

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);
Shady Mohamed Sherif
  • 15,003
  • 4
  • 45
  • 54
-1

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">
Alex83690
  • 758
  • 9
  • 30
-2

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);
IceMetalPunk
  • 5,476
  • 3
  • 19
  • 26
-2

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.

Jennis Vaishnav
  • 331
  • 7
  • 29
  • 3
    Terrible solution in terms of performance and UX – Guerric P Oct 23 '19 at 08:26
  • @Twistingnether please elaborate how? – Jennis Vaishnav Oct 23 '19 at 08:30
  • 2
    I mean your solution is to check periodically, which means potential useless code execution, and the user will always have a delay between the first and the second field – Guerric P Oct 23 '19 at 08:33
  • 3
    @Twistingnether I think you didn't noticed that interval is 500 milliseconds, that's equal to 0.5 second, so there's no chance of delay, Even if you think is slow, then you can use 0.1 second as delay, so I think the user won't have any delay until they are operated by humans. – Jennis Vaishnav Oct 23 '19 at 08:36
  • 2
    It still implies to choose between low performance or low UX – Guerric P Oct 23 '19 at 08:37
  • @Twistingnether with all due respect,sadly , I disagree with your words for low performance and UX. I find my code robust and fast which fits the requirements of the questioner. – Jennis Vaishnav Oct 23 '19 at 08:42
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/201306/discussion-between-jennis-vaishnav-and-twisting-nether). – Jennis Vaishnav Oct 23 '19 at 08:43