Dash Callback on Page Exit

Hi!

I am currently developing an app using Dash Python that has users upload files that will then be placed in an app directory for temporary storage to be used for visualization in a callback. After the callback, the files are then deleted.
The issue is that there may be instances in which the files may still linger, whether that may be through an interruption before the callback, the user exits the page preemptively, etc. In order to alleviate this issue, I was thinking that I would keep a record of all paths created and then have the app just shutil.rmtree all these paths upon the user exiting the app.
I have tried searching through the internet, but can’t seem to find information about this type of capability existing. Is it possible to have a callback be run upon a user exiting the webpage/refreshing the webpage?

Thank you!

Hello @hypervalent,

Welcome to the community!

You are looking for a combo, a window listener on JavaScript that on beforeunload clicks a button that triggers your callback.

1 Like

Thanks for the warm welcome @jinnyzor !

This is exactly what I am looking for thank you!
I’m just confused on how I would go about implementing this, as it seems this is an HTML/JS component rather than a Dash Python component. Would I need to make a separate JS file for this? Or is there a way to implement it into app.py?

1 Like

Yes, it is a separate component not in the Dash app. It needs to be a separate file.

Make a folder called assets in the same directory as the app, things in this folder will auto load to the site.

1 Like

Hey @jinnyzor

Sorry for the late response!
The solution to use a separate JS file in the ‘assets’ directory raises another question for me.
All the history of directories/files to be deleted through this onbeforeunload event are being logged as a list by a html.Div component in Dash, as having separate users using the app at once would mean personalized logs would be useful.
Is there a way for this JS file to get the children from this html.Div component? Assuming that the JS file containing the onbeforeunload event is coded as follows:

<!DOCTYPE html>
<html>
<body onbeforeunload="return myFunction()">
    
<script>
function myFunction() {
  /// Take list from children of html.Div and delete files by iterating through this list
}
</script>

</body>
</html>

Thank you!

1 Like

Not entirely sure what you mean by this…

Yes, javascript is able to remove all the elements from a layout: $('body').empty() would remove all the children.

You are trying to log what specific interactions with the user? Custom layouts can be save utilizing authentication if necessary.

Ah sorry, I should have explained better.

So basically I have an html.Div component that logs all the files/directories that are created by the user during their session. For example, if we have files with paths: file1.txt, file2.txt, file3.txt, then the html.Div component would contain the string representation of this list of files: “[file1.txt, file2.txt, file3.txt]”.
In this case, I was hoping that the onbeforeunload event would be able to pull this list from the html.Div and iterate through it in order to purge the files on unload.

Yes.

However, since this is more than likely stored on the server side, your onbeforeunload would have to fire an event (clicking a button is easy) and trigger the callback on the server with the children of that div as a state, and then you could remove all of those files that way.

I’m sorry it is now my turn to say I’m not entirely sure what you mean by this.

So you are saying that it is not possible to pass the children of the div as a state without using a Dash callback? Would this not work if the user unloads the session by exiting or refreshing the webpage without clicking the specific button to trigger the callback?

You are asking to be notified when the browser closes, this requires an event listener on the client.

You are wanting to use this to dump the user’s data from your server’s file system (correct?) In order to do so, you’d have to have something that triggers it to do so. An invisible button that gets triggered from the onbeforeunload like this:

document.addEventListener("onbeforeunload", function() {document.getElementById('hiddenButton').dispatchEvent(new Event("click")})

If you are talking about removing elements after close, the system will automatically do that, because it is a new session.

Ah I see what you mean.

So as long as I just put that one line of code into a JS file in the ‘assets’ directory, it will call the callback associated with this hidden button?

1 Like

HI jinnyzor

I am also trying to achieve same thing ( capturing the Brower close event in dash app).
i don’t know js and not getting how to use (i already placed the java script file in asset folder)

and added one callback with Input(‘hiddenButton’,‘click’) but my callback is not getting called .

it will be really helpful you you can give some sample code ( dash app code with callback to capture close event).

Thanks in advance

Hello @Awadhesh,

Welcome to the community.

Could you please post your JS code and also your app.py?

Thanks Jinnyzor,

for quick response ,
Here is my sample app code
app.py

import dash
from dash import html
import dash_bootstrap_components as dbc
from dash import Input, Output, State, ALL

app = dash.Dash(prevent_initial_callbacks=True)

app.layout = html.Div(
[
dbc.Button(id=“hiddenButton”),
html.Div(id=“out”),
]
)
@app.callback(Output(“out”, “children”),
Input(“hiddenButton”, “n_clicks”),
)
def app_update(click):
print(“close called”)

app.run_server(debug=True, port=“7777”)

and java script file:

document.addEventListener(“onbeforeunload”, function() {document.getElementById(‘hiddenButton’).dispatchEvent(new Event(“n_clicks”)})

i am not getting the callback when i am closing the tab or closing the browser
i need callback to be called in browser/tab close

Hope i am able to explain it properly

Ah.

I your JS code, the event you are dispatching should be click not n_clicks.

n_clicks is a dash only property that counts how many times a button has been pushed. :grin:

Hi Jinnyzor,
Yes i made that changes intentionally.
because when i am using ‘click’ in callback , then i am getting below error message
"
Property “click” was used with component ID:
“hiddenButton”
in one of the Input items of a callback.
This ID is assigned to a dash_html_components.Button component
in the layout, which does not support this property.
This ID was used in the callback(s) for Output(s):
out.children
"

please suggest how to handle ‘click’ event in dash callback.
it will be really helpful if you shared dash callback code to handle ‘click’ event.

Thanks in advance

Hi @hypervalent ,

did you able to implement the solution for this ?

if Yes , could you please share how are you able to capture browser close event in dash app.

it will be good if you can share some sample code.

Thanks in advance

No, it should be click in the JS code, and it should be n_clicks on the dash side.

Please try that. :blush:

Hello @Awadhesh

I was able to successfully implement it but I had to make a few changes to the line of code according to other StackOverflow posts I saw regarding this same issue.
Unfortunately, I have long since deleted my code for that since I found a workaround option not needing on page exit callbacks, sorry.
I can try to get a working example back up again, but I kind of forgot which changes I made.

no , still its not working. :frowning: