1

When using this code:

document.addEventListener("keydown", function(e) {
    if (e.keyCode == 9)
    {        
        storage.get("items", function(obj) { // this is an asynchronous call
            if (some_condition(obj)) {
                e.preventDefault();
                e.stopPropagation();
            }
        });
    } 
}

if some_condition (depending on the result of the asynchronous call) is True, e.preventDefault(); and e.stopPropagation(); will be executed too late because storage.get is asynchronous.

What option is possible to decide if preventDefault() should be called or not after the result of an asynchronous call?

I was thinking about this solution:

document.addEventListener("keydown", function(e) {
    if (e.keyCode == 9)
    {        
        e.preventDefault();  // always preventDefault and stopPropogation, 
        e.stopPropagation(); // we'll maybe restore it later
        storage.get("items", function(obj) { // this is an asynchronous call
            if (!some_condition) {
                 e.reenableDefault();      // contrary of preventDefault
                 e.restartPropagation();   // contrary of stopPropagation
            }
        });
    } 
}

but obviously there is no e.restartPropagation(); function!

Note: I already read How to reenable event.preventDefault? but this did not help in this context.

Basj
  • 41,386
  • 99
  • 383
  • 673
  • 2
    Just trigger a new event if necessary (assuming you're not able to directly call what the event was supposed to achieve but then your application doesn't seem well designed there). – Denys Séguret Jun 29 '18 at 15:22
  • You could poll the asynchronous function independently of user input at an appropriate time interval and store it in a synchronously accessible context for checking the condition within the event handler synchronously. – Patrick Roberts Jun 29 '18 at 15:23
  • 1
    What you want to do is not really possible. You will need to rethink what you are trying to do. – epascarello Jun 29 '18 at 15:26
  • @DenysSéguret how to *retrigger* a "TAB" keypress that "looks like" a normal TAB keypress fired in a textarea / contenteditable? – Basj Jun 29 '18 at 15:37
  • @PatrickRoberts I was thinking about this: do the asynchronous call independently and fill the result in a variable (accessible from keypress eventlistener), but this requires to do either a frequent (each 5 seconds) call to this async function **or** suffer from non-freshly-updated content... It looked like a non-optimal solution. Any idea about that? – Basj Jun 29 '18 at 15:38
  • @Basj look for document.createEvent, initEvent and dispatchEvents if you want to send events. This works well. – Denys Séguret Jun 29 '18 at 15:42
  • @Basj I'm tempted to label this as an XY problem. It's difficult to tell what you're trying to accomplish, and with more details about the specifics, it might be possible for us to recommend a viable alternative approach that may require a bit of restructuring your current implementation to eliminate this awkward impasse. – Patrick Roberts Jun 29 '18 at 15:42
  • Have you tried to accomplish default action by javascript manually? – Hikmat G. Jun 29 '18 at 16:53
  • @PatrickRoberts: Here is the gist of it: When TAB is pressed in a textarea, it 1) loads the templates/presets stored in Chrome extension `storage` zone 2) Check if the last word before cursor matches one of the presets and then replace the last word by a template (and **then** preventDefault() for TAB). 3) If the word before cursor doesn't match, then **don't** preventDefault() for TAB. – Basj Jun 29 '18 at 22:01
  • In that case I think it would be sufficient to pre-load at least all the template _names_ on page load so you can do the check synchronously, and determine whether or not you need to load the template asynchronously. – Patrick Roberts Jun 29 '18 at 23:15
  • @PatrickRoberts Maybe I can load all templates in a global array (then I can check in this array before doing preventDefault), and then add a Listener if the Chrome extension storage zone (containing templates) are modified: https://stackoverflow.com/a/46160185. This would allow having always the latest version of the templates, and avoid this async stuff in the keypress Listener containing preventDefault. What do you think? – Basj Jun 30 '18 at 14:10
  • @Basj depends on how much memory you expect that to use. – Patrick Roberts Jun 30 '18 at 18:37
  • @PatrickRoberts memory shouldn't be a problem: templates are small (maybe total of all templates: < 10 kb) – Basj Jun 30 '18 at 21:02

1 Answers1

1

Disclaimer: This is a hacky solution and I actually ended up not using it because it became unnecessary to have this behavior.

I had a similar issue on a click event. My thought was to preventDefault if a flag is true. If I wanted the default event behavior, I set the flag to true and retriggered the event on the element. When that instance is handled (remember to skip the logic that retriggers the event), the flag is set to false again.

Instead of a flag I suppose you could also trigger the event on the parent element.

Thomas Prince
  • 332
  • 1
  • 5