Why is my onchange function called twice when using .focus()?

23,740

Solution 1

A quick fix (untested) should be to defer the call to focus() via

setTimeout(function() { ... }, 0);

until after the event handler has terminated.

However, it is possible to make it work without such a hack; jQuery-free example code:

<head>
<style>
input { display: block; }
</style>
<body>
<div></div>
<script>
var div = document.getElementsByTagName('div')[0];

var field = document.createElement('input');
field.type = 'text';
field.onchange = function() {
    // only add a new field on change of last field
    if(this.num === div.getElementsByTagName('input').length)
        div.appendChild(createField(this.num + 1));

    this.nextSibling.focus();
};

function createField(num) {
    var clone = field.cloneNode(false);
    clone.num = num;
    clone.onchange = field.onchange;
    return clone;
}

div.appendChild(createField(1));
</script>

Solution 2

There's a way easier and more reasonable solution. As you expect onchange fire when the input value changes, you can simply explicitly check, if it was actually changed.

function onChangeHandler(e){
  if(this.value==this.oldvalue)return; //not changed really
  this.oldvalue=this.value;
  // .... your stuff
}

Solution 3

I can confirm myOnChange gets called twice on Chrome. But the context argument is the initial input field on both calls.

If you remove the alert call it only fires once. If you are using the alert for testing only then try using console instead (although you need to remove it for testing in IE).

EDIT: It seems that the change event fires twice on the enter key. The following adds a condition to check for the existence of the new field.

function myOnChange(context, curNum) {
      nextNum = curNum+1;

      if ($('#prefix_'+nextNum).length) return false;// added to avoid duplication

      $(context.parentNode).append('<input type="text" onchange="return myOnChange(this,'+nextNum+')" id="prefix_'+nextNum+'" >');
      $('#prefix_'+nextNum)[0].focus();
      return false;
}

Update:

The $('#prefix_'+nextNum).focus(); does not get called because focus is a method of the dom object, not jQuery. Fixed it with $('#prefix_'+nextNum)[0].focus();.

Share:
23,740
Nanne
Author by

Nanne

Updated on March 12, 2020

Comments

  • Nanne
    Nanne about 4 years

    TLDR

    • Check this example in chrome.
    • Type someting and press tab. see one new box appear
    • type something and press enter. see two new boxes appear, where one is expected.

    Intro
    I noticed that when using enter rather then tab to change fields, my onchange function on an input field was firing twice. This page was rather large, and still in development (read: numerous other bugs), so I've made a minimal example that shows this behaviour, and in this case it even does it on 'tab'. This is only a problem in Chrome as far as I can tell.

    What it should do
    I want to make a new input after something is entered into the input-field. This field should get focus.

    Example:

    javascript - needing jquery

    function myOnChange(context,curNum){
          alert('onchange start');
          nextNum = curNum+1;
          $(context.parentNode).append('<input type="text" onchange="return myOnChange(this,'+nextNum+')" id="prefix_'+nextNum+'" >');
          $('#prefix_'+nextNum).focus();
          return false;
    }
    

    HTML-part

    <div>
        <input type="text" onchange="return myOnChange(this,1);" id="prefix_1">
    </div>
    

    the complete code is on pastebin. you need to add your path to jquery in the script

    A working example is here on jFiddle

    The onchange gets called twice: The myOnChange function is called, makes the new input, calls the focus(), the myOnChange gets called again, makes a new input, the 'inner' myOnChange exits and then the 'outer' myOnchange exits.

    I'm assuming this is because the focus change fires the onchange()?. I know there is some difference in behaviour between browsers in this.

    I would like to stop the .focus() (which seems to be the problem) to NOT call the onchange(), so myOnChange() doesn't get called twice. Anybody know how?