Search code examples
javascriptdom-eventsevent-bubbling

Custom KeyboardEvent does not bubble


For some reason, my custom KeyboardEvent does not bubble. I attend to handle holding key:

document.addEventListener('DOMContentLoaded', function () {
  var isPressed = false;
  var startTimeStamp;

  document.addEventListener('keydown', function (e) {    
    if (!isPressed) {
      startTimeStamp = e.timeStamp;
      isPressed = true;
    }
  });

  document.addEventListener('keyup', function (e) {
    isPressed = false;

    var eventDuration = e.timeStamp - startTimeStamp;

    var _e = new KeyboardEvent('keyheld', {
      bubbles: true,
      cancelBubble: false,
      cancelable: true,
      char: e.char,
      charCode: e.charCode,
      key: e.key,
      keyCode: e.keyCode,
      which: e.which,
      shiftKey: e.shiftKey,
      ctrlKey: e.ctrlKey,
      altKey: e.altKey,
      metaKey: e.metaKey
    });

    Object.defineProperty(_e, 'detail', {
        value: {
            duration: eventDuration
        },
        enumerable: true
    });

    document.dispatchEvent(_e);
  });
});

And then I can handle keyheld event with a listener attached to document. Nonetheless, a listener attached to anything (below it is body) else does not fire at all:

document.addEventListener('DOMContentLoaded', function () {
    document.body.addEventListener('keyheld', function (e) {
      console.log('KEY HELD: ', e.detail.duration, e);
    });
});

The code above does not work, what even stranger, even with true supplied as the third argument of addEventListener() which should fire a listener when capturing rather than bubbling.

What may be wrong here?


Solution

  • When you call document.dispatchEvent(event), the event is dispatched with its target set to document, which is the top-level node in the DOM hierarchy, as can be seen in an illustration here.

    The event would normally bubble to the parents of the target element, but when the target is document, there are no parents (document.parentNode === null). Trying to register a capturing listener on the body doesn't work for the same reason.

    You probably wanted to dispatch to the target of the original event: e.target.dispatchEvent(_e).