Why is my onchange function called twice when usin

2019-02-25 00:32发布

问题:

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?

回答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>


回答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
}


回答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();.



回答4:

The problem is indeed that because of the focus(), the onchange is called again. I don't know if this is a good sollution, but this adding this to the function is a quick sollution:

context.onchange = "";

(The onchange is called again, but is now empty. This is also good because this function should never be called twice. There will be some interface changes in the final product that help with problems that would arise from this (mistakes and all), but in the end this is something I probably would have done anyway).

sollution here: http://jsfiddle.net/k4WKH/2/

As @johnhunter says, the focus does not work in the example, but it does in my complete code. I haven't looked into what's going on there, but that seems to be a separate problem.



回答5:

maybe this some help to anybody, for any reason, in chrome when you attach an event onchage to a input text, when you press the enterkey, the function in the event, do it twice, i solve this problem chaged the event for onkeypress and evaluate the codes, if i have an enter then do the function, cause i only wait for an enterkey user's, that not works for tab key.

    input_txt.onkeypress=function(evt){ 
            evt = evt || window.event;
            var charCode = evt.which || evt.keyCode;
            if(charCode === 13)  evaluate( n_rows ); 
             };


回答6:

Try this example:

var curNum = 1;
function myOnChange( context )
{
    curNum++;

    $('<input type="text" onchange="return myOnChange( this )" id="prefix_'+ curNum +'" >').insertAfter( context );
    $('#prefix_'+ curNum ).focus();

    return false;
}

jsFiddle.