Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor KeyboardListenerAPI #2087

Merged
merged 4 commits into from Sep 1, 2021
Merged

Refactor KeyboardListenerAPI #2087

merged 4 commits into from Sep 1, 2021

Conversation

bjoluc
Copy link
Member

@bjoluc bjoluc commented Aug 17, 2021

In an approach to https://github.com/jspsych/jsPsych/projects/6#card-64293394, I recently refactored the KeyboardListenerAPI class to type the parameters and clean up the logic quite a bit. I also replaced the two array and object-based solutions for listeners and held keys with ES6 sets, which fit the use case very well and could also improve the performance in scenarios with many listeners.
I'll add some notes as review comments below.

@bjoluc bjoluc requested a review from jodeleeuw August 17, 2021 17:38
@bjoluc
Copy link
Member Author

bjoluc commented Aug 17, 2021

@jodeleeuw Sorry for requesting your reviews so often; low priority.

Comment on lines 62 to 72
reset(root_element: Element) {
this.listeners.clear();
this.heldKeys.clear();
root_element.removeEventListener("keydown", this.rootKeydownListener);
root_element.removeEventListener("keyup", this.rootKeyupListener);
}

createKeyboardEventListeners(root_element: Element) {
root_element.addEventListener("keydown", this.rootKeydownListener);
root_element.addEventListener("keyup", this.rootKeyupListener);
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jodeleeuw These methods are not mentioned in the docs. Any reason for them to be publicly available? I can't think of a scenario in which users would need to call these. Also, calling reset() in a plugin without calling createKeyboardEventListeners() afterwards will break keyboard responses in subsequent trials. I think the root element should be passed to the constructor instead which sets everything up then.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, these were meant to be internal methods. I'm fine with moving to a constructor.

Copy link
Member Author

@bjoluc bjoluc Aug 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few notes on how I implemented this now:

  • Only in the constructor wouldn't have worked since the JsPsych DOM_container is currently only known when run() is called
  • I didn't want to leave a public setup function, so a function is injected into the constructor now that returns the display element, if known. For unit tests (and if we change the DOM_container setup in jsPsych), that function is already invoked in the constructor. If it did not return an element then, the listeners are registered on the first getKeyboardResponse() call when the display element is already known.
  • I used this chance to remove the dependency on JsPsych and the html-keyboard-response plugin in pluginapi.test.ts.
  • When we change the internal JsPsych setup behavior during some future refactoring, we can also get rid of the function injection approach without changing any public API :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the display_element parameter is passed to initJsPsych and not run() couldn't we know the value of DOM_container earlier? I suppose one reason for delaying it is if you wanted to dynamically setup a container between the time that you called initJsPsych and run but I can't think of a reason that you couldn't do the dynamic initialization prior to calling initJsPsych.

There's no legacy reason to not move it to initJsPsych as far as I know, since in v6.x run() and initJsPsych were essentially the same thing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd love to move it, but I just went over the code again and found the blocker: setupDOM() is asynchronous because it waits for the document to be ready before it creates the container elements (#948). So there's no way to reliably know the container elements in the constructor...

@jodeleeuw jodeleeuw added this to the 7.0 milestone Aug 31, 2021
@jodeleeuw jodeleeuw merged commit a66d29c into jspsych:modularization Sep 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants