Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the keyup handler being invoked here, and can I prevent it?

Consider the following HTML5+Javascript:

$(function() {	
    $('#edit').hide();
	
	$('#value')
		.css('cursor', 'pointer')
		.click(function() {
			$('#edit').show();
			$('#edit input').focus();
			$('#value').hide();
		});
	
	$('#edit input')
		.keyup(function(e) {
			if (e.keyCode == 13) {  // <enter>
				$('#value').show();
				$('#edit').hide();
			}
		});
  });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="value">
	<a href="#">hello</a>
</div>
<div id="edit">
	<input type="text" value="hello" />
</div>

Originally, all this did was to capture click events on the text hello, replacing the text with an input box. When the user then hit Enter, the new text would be saved to server via AJAX and the input box again replaced with just the text.

Now I've added the a tag to allow navigation via Tab+Enter rather than only using the mouse (accessibility; yay!), but I'm finding that, when doing so, the call to focus() magically triggers the keyup() event. (I know this because commenting out the call to focus() otherwise results in the desired behaviour.)

The result is that tabbing and entering onto the text opens but then immediately closes the input box, and as far as the user's concerned, nothing has happened at all.

Why is the input's keyup handler being triggered by a click event on a completely unrelated element? How can I stop this from happening?


Working scenario

  1. Click on hello
  2. hello disappears; textbox appears, with caret inside it
  3. Press Enter
  4. Textbox disappears; hello reappears

Broken scenario

  1. Put focus on document
  2. Press Tab until hello is in focus
  3. Press Enter
  4. hello disappears; textbox appears, with caret inside it
  5. Textbox disappears; hello reappears ... before I have a chance to do anything.
like image 942
Lightness Races in Orbit Avatar asked Apr 28 '15 15:04

Lightness Races in Orbit


People also ask

How do you delay the Keyup handler until the user stops typing?

Explanation. Use a variable to store the timeout function. Then use clearTimeout() to clear this variable of any active timeout functions, and then use setTimeout() to set the active timeout function again.

How do I get rid of Keyup event?

How to remove keyup event in jQuery? jQuery unbind() Method The unbind() method was deprecated in version 3.0. Use the off() method instead. The unbind() method removes event handlers from selected elements.

How do you prevent a number from Keydown?

Use the keypress event instead. It's the only key event which will give you information about the character that was typed, via the which property in most browsers and (confusingly) the keyCode property in IE. Using that, you can conditionally suppress the keypress event based on the character typed.

What does Keyup mean?

The keyup event is fired when a key is released. The keydown and keyup events provide a code indicating which key is pressed, while keypress indicates which character was entered. For example, a lowercase "a" will be reported as 65 by keydown and keyup , but as 97 by keypress .


1 Answers

So the problem is that when you press the enter key, the keyup event is fired when you release the key.

This creates a flow like the following: 1, click the link (press enter key) 2, link hides 3, focus is given to text input (enter key is still pressed at this point) 4, release of key (keyup event is fired)

If you change the event to keydown then pressing enter on the link will not conflict the input because that input doesn't have focus.

Additionally, using the same code in the keydown event that you had in the keyup event will still do the exact same thing (ie press enter and the input hides and the link shows).

EDIT

https://jsfiddle.net/geyut7uv/

The only solid solution that I could find, was to create a "blocker" variable that "disables" the keydown event. I've attached it to the link only when the keydown is clicked. This is checked on the keydown event by the input which will be immediately triggered if a user holds the key down.

It's only when the enter key keyup event is triggered that I allow the "blocker" variable pass.

Ideally, I'd like to remove all global scope variables, but I couldn't find a way to recognize that an event (keydown) was currently in use.

<div id="value">
    <a href="#">hello</a>
</div>
<div id="edit">
    <input type="text" value="hello" />
</div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/javascript">
$(function() {
    var enter_enabled = true;
    $('#edit').hide();

    $('#value')
    .css('cursor', 'pointer')
    .click(function() {
        $('#edit').show();
        $('#edit input').focus();
        $('#value').hide();
    }).keydown(function(e){
        enter_enabled = false;
    });

    $('#edit input')
    .keydown(function(e){
        if(e.keyCode == 13 && enter_enabled){
            $('#value').show();
            $('#edit').hide();
        }
    })
    .keyup(function(e){
        if(e.keyCode == 13){
            enter_enabled = true;
        }
    });
});
</script>
like image 163
Samuel Cook Avatar answered Oct 06 '22 00:10

Samuel Cook