Description
Description
During the development of a Typeahead component which attaches itself visually to the bottom of an ui.input field to (later) show search result data from a DB, i stumbled over a potential bug when modifying certain event_listeners such as keydown
and keyup
via the element.on
method "post creation".
Situation:
- An ui.input field is created
- A focus handler is set up via
.on("focus", handle_focus)
and a blur_handler is setup viaon("blur", handle_blur)
- In
handle_focus
the.on("keydown", handle_keydown)
is assigned to track cursor key presses - In
handle_blur
the event listener is removed again.- As NiceGUI doesn't officially support removal of event listeners added via
.on
yet there is a helper class involved which "cleans up again" / can unassign event listeners, but that just as side note and has no impact on this report / test as you can see in the example code attached below.
- As NiceGUI doesn't officially support removal of event listeners added via
- Expected behavior: handle_keydown will be called when a key is pressed.
- Actual behavior: It is not
If a "keypress" or "keyup" listener is assigned later and not directly upon creation of the input field, so e.g. delayed by a timer or an event, then the handlers will never be called. If they are assigned directly upon creation, so before everything goes through the websocket from Python to JS, then they work though.
If I assign the same events directly via JavaScript to the DOM element w/ help of the new getHtmlElement
function though then everything works perfectly fine. Thus there seems to be some synchronization kind of issue for certain events. A delayed assigned blur
event just for example works like a charm.
from nicegui import ui
@ui.page('/')
async def index():
log_area = ui.scroll_area().style('height: 200px; width: 400px')
def log_key(e):
with log_area:
ui.label(f"Key pressed: {e.args['key']}").classes('font-mono text-xs').style('line-height: 1; margin: 0; padding: 0;')
log_area.scroll_to(percent=1.0)
with ui.row():
input1 = ui.input()
ui.run_javascript(f'''
getHtmlElement({input1.id}).addEventListener("keydown", () => console.log("keydown on input1 via js"));
''')
input1.on('keydown', log_key) # will be triggered
input1.on('keydown', js_handler='() => console.log("keydown on input1")') # will be triggered
input2 = ui.input()
def attach_event():
input2.on('keydown', log_key) # will NOT be triggered
input2.on('keydown', js_handler='() => console.log("keydown on input2")') # will NOT be triggered
input2.on('blur', lambda e: print('blur')) # will be triggered
ui.run_javascript(f'''
getHtmlElement({input2.id}).addEventListener("keydown", () => console.log("keydown on input2 via js"));
''') # will be triggered
# setup timer which attaches the event
ui.timer(1.0, attach_event, once=True, immediate=False)
ui.run()
Metadata
Assignees
Type
Projects
Status
Todo