Opened 3 years ago

Last modified 2 years ago

#18984 assigned defect

Tree keydown event handling is flaky

Reported by: donhatch Owned by: dylan
Priority: high Milestone: 1.14
Component: Events Version: 1.9.1
Keywords: Cc:
Blocked By: Blocking:

Description

Five out of the six ways I know of to make a keydown event handler on a Tree seem to be unreliable, starting in dojo 1.9.1. This was observed using chrome version "57.0.2987.74 beta (64-bit)" on a macbook pro, using dojo 1.9.1, 1.9.10, 1.10.1, 1.10.7, 1.11.1, 1.11.3, 1.12.1. (It seems to work properly in 1.8.13.)

I'll attach a test program that demonstrates, using key handlers connected in various ways.

I didn't bother with anything involving dojo.connect(), since that's deprecated. And I didn't set tree.onKey*, since that apparently prevents tree.on(...) and on(tree, ...) from working. That leaves six other ways to do it (I think):

  • (A) Tree ctor params.onKey* = function(e){...}
  • (B) tree.on('key*', function(e){...})
  • (C) on(tree, 'key*', function(e){...})
  • (D) on(tree.domNode, 'key*', function(e){...})
  • (E) tree.domNode.onkey* = function(e){...}
  • (F) tree.domNode.addEventListener('key*', function(e){...})

Only (A) seems to work reliably. To demonstrate, load the page and do the following:

  1. Click anywhere on the tree to give it keyboard focus
  2. Press-and-quickly-release the 'j' key
  3. A quarter-second later, press-and-quickly-release the space bar.

Console output is as follows. Note the missing handler calls (B)-(F) on the keydown of the Space bar.

    ============================================
    (+3.209525s) (A) tree ctor params.onKeyDown: KeyJ
    (+0.000840s)  (B) tree.on 'keydown' callback: KeyJ
    (+0.000380s)   (C) on tree 'keydown' callback: KeyJ
    (+0.000385s)    (D) on tree.domNode 'keydown' callback: KeyJ
    (+0.000330s)     (E) tree.domNode.onkeydown: KeyJ
    (+0.000280s)      (F) tree.domNode.addEventListener 'keydown' callback: KeyJ
    (+0.001215s) (A) tree ctor params.onKeyPress: KeyJ
    (+0.001900s)  (B) tree.on 'keypress' callback: KeyJ
    (+0.000445s)   (C) on tree 'keypress' callback: KeyJ
    (+0.000320s)    (D) on tree.domNode 'keypress' callback: KeyJ
    (+0.000285s)     (E) tree.domNode.onkeypress: KeyJ

Attachments (1)

treeKeyEventBug.html (7.6 KB) - added by donhatch 3 years ago.
treeKeyEventBug.html - program to demonstrate #18984

Download all attachments as: .zip

Change History (5)

Changed 3 years ago by donhatch

Attachment: treeKeyEventBug.html added

treeKeyEventBug.html - program to demonstrate #18984

comment:1 Changed 3 years ago by donhatch

What is up with the initial bug description getting truncated? The same thing happened when I filed #18977. Fortunately I was prepared for something to go wrong this time so I copied and saved the description before I posted it; here it is again:


Five out of the six ways I know of to make a keydown event handler on a Tree seem to be unreliable, starting in dojo 1.9.1. This was observed using chrome version "57.0.2987.74 beta (64-bit)" on a macbook pro, using dojo 1.9.1, 1.9.10, 1.10.1, 1.10.7, 1.11.1, 1.11.3, 1.12.1. (It seems to work properly in 1.8.13.)

I'll attach a test program that demonstrates, using key handlers connected in various ways.

I didn't bother with anything involving dojo.connect(), since that's deprecated. And I didn't set tree.onKey*, since that apparently prevents tree.on(...) and on(tree, ...) from working. That leaves six other ways to do it (I think):

  • (A) Tree ctor params.onKey* = function(e){...}
  • (B) tree.on('key*', function(e){...})
  • (C) on(tree, 'key*', function(e){...})
  • (D) on(tree.domNode, 'key*', function(e){...})
  • (E) tree.domNode.onkey* = function(e){...}
  • (F) tree.domNode.addEventListener('key*', function(e){...})

Only (A) seems to work reliably. To demonstrate, load the page and do the following:

  1. Click anywhere on the tree to give it keyboard focus
  2. Press-and-quickly-release the 'j' key
  3. A quarter-second later, press-and-quickly-release the space bar.

Console output is as follows. Note the missing handler calls (B)-(F) on the keydown of the Space bar.

    ============================================
    (+3.209525s) (A) tree ctor params.onKeyDown: KeyJ
    (+0.000840s)  (B) tree.on 'keydown' callback: KeyJ
    (+0.000380s)   (C) on tree 'keydown' callback: KeyJ
    (+0.000385s)    (D) on tree.domNode 'keydown' callback: KeyJ
    (+0.000330s)     (E) tree.domNode.onkeydown: KeyJ
    (+0.000280s)      (F) tree.domNode.addEventListener 'keydown' callback: KeyJ
    (+0.001215s) (A) tree ctor params.onKeyPress: KeyJ
    (+0.001900s)  (B) tree.on 'keypress' callback: KeyJ
    (+0.000445s)   (C) on tree 'keypress' callback: KeyJ
    (+0.000320s)    (D) on tree.domNode 'keypress' callback: KeyJ
    (+0.000285s)     (E) tree.domNode.onkeypress: KeyJ
    (+0.000300s)      (F) tree.domNode.addEventListener 'keypress' callback: KeyJ
    ............................................ (>=.002 seconds elapsed)
    (+0.047565s) (A) tree ctor params.onKeyUp: KeyJ
    (+0.000645s)  (B) tree.on 'keyup' callback: KeyJ
    (+0.000245s)   (C) on tree 'keyup' callback: KeyJ
    (+0.000340s)    (D) on tree.domNode 'keyup' callback: KeyJ
    (+0.000230s)     (E) tree.domNode.onkeyup: KeyJ
    (+0.000230s)      (F) tree.domNode.addEventListener 'keyup' callback: KeyJ
    -------------------------------------------- (>=.1 seconds elapsed)
    (+0.200255s) (A) tree ctor params.onKeyDown: Space
>>>>>> (MISSING HANDLER CALLS (B)-(F) HERE!) <<<<<<
    ............................................ (>=.002 seconds elapsed)
    (+0.054655s) (A) tree ctor params.onKeyUp: Space
    (+0.000575s)  (B) tree.on 'keyup' callback: Space
    (+0.000225s)   (C) on tree 'keyup' callback: Space
    (+0.000210s)    (D) on tree.domNode 'keyup' callback: Space
    (+0.000195s)     (E) tree.domNode.onkeyup: Space
    (+0.000205s)      (F) tree.domNode.addEventListener 'keyup' callback: Space
    ============================================ (>=1 seconds elapsed)

The expected output can be obtained by waiting 2 seconds instead of a quarter-second in step 3:

  ============================================
  (+7.732755s) (A) tree ctor params.onKeyDown: KeyJ
  (+0.000860s)  (B) tree.on 'keydown' callback: KeyJ
  (+0.000345s)   (C) on tree 'keydown' callback: KeyJ
  (+0.000410s)    (D) on tree.domNode 'keydown' callback: KeyJ
  (+0.000340s)     (E) tree.domNode.onkeydown: KeyJ
  (+0.000325s)      (F) tree.domNode.addEventListener 'keydown' callback: KeyJ
  (+0.001230s) (A) tree ctor params.onKeyPress: KeyJ
  (+0.001895s)  (B) tree.on 'keypress' callback: KeyJ
  (+0.000400s)   (C) on tree 'keypress' callback: KeyJ
  (+0.000310s)    (D) on tree.domNode 'keypress' callback: KeyJ
  (+0.000295s)     (E) tree.domNode.onkeypress: KeyJ
  (+0.000290s)      (F) tree.domNode.addEventListener 'keypress' callback: KeyJ
  ............................................ (>=.002 seconds elapsed)
  (+0.015955s) (A) tree ctor params.onKeyUp: KeyJ
  (+0.000495s)  (B) tree.on 'keyup' callback: KeyJ
  (+0.000210s)   (C) on tree 'keyup' callback: KeyJ
  (+0.000235s)    (D) on tree.domNode 'keyup' callback: KeyJ
  (+0.001385s)     (E) tree.domNode.onkeyup: KeyJ
  (+0.000240s)      (F) tree.domNode.addEventListener 'keyup' callback: KeyJ
  ============================================ (>=1 seconds elapsed)
  (+2.159635s) (A) tree ctor params.onKeyDown: Space
  (+0.000515s)  (B) tree.on 'keydown' callback: Space
  (+0.000240s)   (C) on tree 'keydown' callback: Space
  (+0.000330s)    (D) on tree.domNode 'keydown' callback: Space
  (+0.000280s)     (E) tree.domNode.onkeydown: Space
  (+0.000280s)      (F) tree.domNode.addEventListener 'keydown' callback: Space
  ............................................ (>=.002 seconds elapsed)
  (+0.044530s) (A) tree ctor params.onKeyUp: Space
  (+0.000825s)  (B) tree.on 'keyup' callback: Space
  (+0.000175s)   (C) on tree 'keyup' callback: Space
  (+0.000170s)    (D) on tree.domNode 'keyup' callback: Space
  (+0.000140s)     (E) tree.domNode.onkeyup: Space
  (+0.000150s)      (F) tree.domNode.addEventListener 'keyup' callback: Space
  ============================================ (>=1 seconds elapsed)

Or, use a Button instead of a Tree to get the expected output:

  ============================================
  (+5.709070s) (A) button ctor params.onKeyDown: KeyJ
  (+0.000680s)  (B) button.on 'keydown' callback: KeyJ
  (+0.000375s)   (C) on button 'keydown' callback: KeyJ
  (+0.000310s)    (D) on button.domNode 'keydown' callback: KeyJ
  (+0.000270s)     (E) button.domNode.onkeydown: KeyJ
  (+0.000275s)      (F) button.domNode.addEventListener 'keydown' callback: KeyJ
  (+0.001280s) (A) button ctor params.onKeyPress: KeyJ
  (+0.000500s)  (B) button.on 'keypress' callback: KeyJ
  (+0.000370s)   (C) on button 'keypress' callback: KeyJ
  (+0.000290s)    (D) on button.domNode 'keypress' callback: KeyJ
  (+0.000250s)     (E) button.domNode.onkeypress: KeyJ
  (+0.000245s)      (F) button.domNode.addEventListener 'keypress' callback: KeyJ
  ............................................ (>=.002 seconds elapsed)
  (+0.041980s) (A) button ctor params.onKeyUp: KeyJ
  (+0.000630s)  (B) button.on 'keyup' callback: KeyJ
  (+0.000295s)   (C) on button 'keyup' callback: KeyJ
  (+0.000295s)    (D) on button.domNode 'keyup' callback: KeyJ
  (+0.000285s)     (E) button.domNode.onkeyup: KeyJ
  (+0.000260s)      (F) button.domNode.addEventListener 'keyup' callback: KeyJ
  -------------------------------------------- (>=.1 seconds elapsed)
  (+0.247060s) (A) button ctor params.onKeyDown: Space
  (+0.000605s)  (B) button.on 'keydown' callback: Space
  (+0.000285s)   (C) on button 'keydown' callback: Space
  (+0.000265s)    (D) on button.domNode 'keydown' callback: Space
  (+0.000210s)     (E) button.domNode.onkeydown: Space
  (+0.000235s)      (F) button.domNode.addEventListener 'keydown' callback: Space
  ............................................ (>=.002 seconds elapsed)
  (+0.045490s) (A) button ctor params.onKeyUp: Space
  (+0.000715s)  (B) button.on 'keyup' callback: Space
  (+0.000240s)   (C) on button 'keyup' callback: Space
  (+0.000215s)    (D) on button.domNode 'keyup' callback: Space
  (+0.000205s)     (E) button.domNode.onkeyup: Space
  (+0.000190s)      (F) button.domNode.addEventListener 'keyup' callback: Space
  ============================================ (>=1 seconds elapsed)

comment:2 Changed 3 years ago by donhatch

I bisected it down to this commit: https://github.com/dojo/dijit/commit/135a9d1eeed663142b46fb67911c94eaa376a848

commit 135a9d1eeed663142b46fb67911c94eaa376a848 Author: Douglas Hays <doughays@…> Date: Thu Apr 4 14:40:06 2013 +0000

Refs #16925. IE8 requires preventDefault of keydown SPACE to prevent scrolling but that cancels the subsequent keypress event. Create _keyboardSearch method from _onContainerKeypress that can be called from keydown so that SPACE can be processed there.

git-svn-id: http://svn.dojotoolkit.org/src/dijit/trunk@31150 560b804f-0ae3-0310-86f3-f6aa0a117693

comment:3 Changed 3 years ago by dylan

Milestone: tbd1.12.3
Priority: undecidedhigh
Status: newassigned

"What is up with the initial bug description getting truncated?"

It's trac's built-in spam prevention tool. I wish it was better than it is.

We're planning to do a round of point releases (e.g. 1.12.2) within the next few days, so we'll look into this after that release. Thanks for taking the time to bisect the issue.

comment:4 Changed 2 years ago by dylan

Milestone: 1.12.31.14
Note: See TracTickets for help on using tickets.