ClassName toggle via JS Event Listener not registering in callback

Hi everyone,

im toggling the class of a div via Dash n_clicks to make it appear selected:

@app.callback(
    Output("div1", "className", allow_duplicate=True),
    Input("div1", "className"), 
    Input("div1", "n_clicks"), 
    prevent_initial_call=True)
def toggle_1(className, n_clicks):
    if 'selected' in className:
        return className.replace('selected','')
    else:
        return className + ' selected'

I was asked to add keyboard support for the selection as well, so I added a custom JS script with a event key listener, which works fine! (I didn’t get to grips with the dash extension and JS seemed simpler):

document.addEventListener('keydown', (event) => {
    var name = event.key;
    var code = event.code;
    if (code.includes('ArrowLeft')) {
        var element = document.getElementById("div1");
        element.classList.toggle("selected");}}, false);

However, lets say I de-select the div with a key press and re-select it with a click, the Dash callback does not register the toggled class name and gives me the class name as if the keypress had not happened. Is there some server caching going on that I can disable, or how do I best go about this? Would really appreciate some feedback here!

Best,
Max

Hi, wouldn’t it make more sense to use the div1 className as a State rather than an input (in the callback).

Also you might want to add some additional logging or perform some debugging to see which code paths are actually executed (although the outcome might be obviously wrong, for now)

Regards,
J.

I just changed it to State and its still the same problem. How would you step through this in the debugger? Breakpoints and logging outputs inside the dash callback show that the className which is passed into the callback is wrong, ie its not the one I toggled and its also not the one shown on the div in the inspected element in chrome.

Hello @mkdash,

Welcome to the community!

What you are experiencing is a syncing issue better react and the dom.

You should instead do something like this:

document.addEventListener('keydown', (event) => {
    var name = event.key;
    var code = event.code;
    if (code.includes('ArrowLeft')) {
        var element = document.getElementById("div1");
        element.dispatchEvent(new Event('click'))

If that doesnt work, then use it like this:

document.addEventListener('keydown', (event) => {
    var name = event.key;
    var code = event.code;
    if (code.includes('ArrowLeft')) {
        var element = document.getElementById("div1");
        element.dispatchEvent(new Event('mousedown'))
        element.dispatchEvent(new Event('click'))
        element.dispatchEvent(new Event('mouseup'))

Since your div is already registering with n_clicks. :slight_smile:

3 Likes

Hi @jinnyzor ,

thanks for welcoming me and thank you for your input! I’ve built some fun and some very useful things with Dash so far, this time round I might feel a little too comfortable and misuse it for an MVC style problem, albeit a simple one…

Sadly your code suggestion did not work, both ways neither registered in the frontend nor in the backend… however (!), this little guy does the trick:

document.addEventListener('keydown', (event) => {
    var name = event.key;
    var code = event.code;
    if (code.includes('ArrowLeft')) {
        var element = document.getElementById("div1");
        element.click()

Thanks everyone!

1 Like

Ah, I’ve had issues with just using click(), there is something about how react doesn’t recognize is all the time.

It works with jquery though, which is nice.

My original code probably just needed some arguments, like bubbles: true.

You can check a post where I listen to the escape key to reset charts like they were double clicked.