1

I'd like to have two (or even more) identical html forms on my website.
For example, one - at the top, and another - at the bottom of the page.
What i want is for them to have exactly the same content at any given time. If user changes a value in one of them, all the rest are updated.
Is there a better way to synchronize then javascript onchange/onkeyup events?
onchange fires only after element loses focus.
onkeyup is not perfect too. It wastes a lot of cpu on long text copying and won't fire if element lost focus between onkeydown and onkeyup events.

user2052437
  • 173
  • 1
  • 1
  • 8

1 Answers1

2

Update: I just learned that HTML5 has a more appropriate event for this, oninput, but of course it doesn't work in older browsers. oninput will detect any kind of text change, including Paste, Cut, and Delete from the right-click menu. So the best option may be to check if the browser supports oninput (e.g. by using the function recommended here), falling back to the below method if not. On older versions of IE, onpropertychange can be used to simulate oninput.


I decided to change my answer, based on how KnockoutJS accomplishes this. From this page in the Knockout docs:

"afterkeydown" - updates your view model as soon as the user begins typing a character. This works by catching the browser’s keydown event and handling the event asynchronously.

Of these options, "afterkeydown" is the best choice if you want to keep your view model updated in real-time.

It accomplishes the asynchronous behavior by using setTimeout with a time value of zero. Other than that, it appears to be just like a regular keydown event handler.

Here's a simple example, using jQuery, which I believe behaves equivalently to Knockout's "afterkeydown" event:

$('#email').keydown(function() {
    setTimeout( $.proxy(handler, this), 0);
});

function handler() {
    console.log( this.value );
}

Note: This will not catch right-click paste events and drag-and-drop events. If you want to update the text on those events too, simply listen for them in the same manner as keydown, e.g.:

$('#email').on('keydown paste drop', function() {
    setTimeout( $.proxy(handler, this), 0);
});

Like keydown, paste and drop also need the setTimeout in order to update with the latest value of the text.

Original answer:

onkeyup is probably the way to go, but you raise a good point about it not firing if the element loses focus between keydown and keyup. Based on this answer, I'm pretty sure the solution would be to listen for the keyup event on a container element (or on the body, although in this case it would probably make the most sense to bind it to the <form> element).

As to CPU usage on paste, you could try canceling the event unless a certain amount of time has passed (say 50 ms)...hopefully that will be sufficient. If not, you could look at how some of the popular 2-way data-binding frameworks handle this...most of the ones I've seen use onkeyup.

Community
  • 1
  • 1
Matt Browne
  • 12,169
  • 4
  • 59
  • 75
  • For those who are interested, this is the relevant source file in knockout.js: https://github.com/SteveSanderson/knockout/blob/master/src/binding/defaultBindings/value.js (search for "keydown"). – Matt Browne Feb 09 '13 at 06:52
  • This method still is not exactly fast, but i don't think it is possible to speed it up without using of pointers, which javascript lacks. Otherwise this works fine, thank you. – user2052437 Feb 09 '13 at 07:07
  • The "afterkeydown" approach seemed to respond faster for me than listening on keyup and blur (blur instead of change because of the lost focus before keyup issue you described)...I would guess that might not be true if there are other things going on in the background however. Those are really the only ways to have Javascript respond immediately to text changes. – Matt Browne Feb 09 '13 at 14:49