How to auto-scroll down to end of Text Area when new text is added

Hey there!

I’ trying to display logs on a text area and was wondering if it was possible to force it to automatically scroll down to the end when adding new logs.

Any Ideas ? :smiley:

Edit:

I’ve tried the flex-box trick proposed in this topic, but it didn’t do it, maybe I’m missing something obvious…

html.Div(id='textarea_container',
     style={
            'overflow': 'auto',
            'display': 'flex',
            'flex-direction': 'column-reverse'},
    children=[
            dcc.Textarea(id='console_out_textarea',
                              style={'width': '100%', height': 400}),
    ]),

My guess is that the flex column-reverse trick applies at the element level in the DOM, but not on the contents of a textarea, which is just a single string.

Googling around suggests that to do this on a textarea contents might require javascript.

Ok, thanks for the Help :slight_smile:

Hello @jajarbins, I am facing the same issue. I’d like to know by any chance if you found a solution?

Thx for your help

Hi, you can try using visdcc.Run_js to write your own javascript event listener and action. :slightly_smiling_face:
here is the example code https://github.com/jimmybow/visdcc/blob/master/example/Run_js/Add_event_and_callback.py

2 Likes

I achieved this using @jimmybow recommendation, by using visdcc.Run_js. I use Dash to run a huge (~25k lines) physics code for tokamaks. This code has many scripts that are not directly accessible to the GUI class, and they all use python’s logging module to write to a log text file. Because some of these scripts run for a long time, the user needed to be able to see where the code is at in the run when accessing from a web browser. So I just put the logfile into an html5 textarea.

I have a dcc.Interval running every 5 seconds to read the log file and then put it into an dcc.Textarea. Every time this interval runs, I also call the visdcc.Run_js to send the textarea to the bottom of the scrollable area. I’v included some code below to help illustrate this, but keep in mind that this is not a complete example (my main DASH script is ~2200 lines long).

app.layout = html.Div(
        id="big-app-container",
        children=[
            #===interval for updating logFile every 5 seconds
            dcc.Interval(
                id='intervalLog',
                interval=5*1000, # in milliseconds
                n_intervals=0
                ),
            #===visdcc to run js 
            visdcc.Run_js(id = 'javascriptLog', run = ""),
            #=== your other code
            runCode()

        ],
)

Then in the dash app where I wanted the log file i put a callback that has the visdcc.Run_js as an output (obviously your classNames will be different)

def buildLogTab():
        return html.Div(
            id="logTab",
            children=[
                        html.H4("Log File Updated Every 5 Seconds"),
                        dcc.Textarea(id="logData", value='Initializing...', className="logBox",
                            readOnly=True),
                    ],

            className="wideBoxDark"
            )

@app.callback([Output('logData', 'value'),
               Output('javascriptLog', 'run')],
              [Input('intervalLog', 'n_intervals')])
def updateLogFile(n):
    if n < 1:
        raise PreventUpdate
    with open(logFile, "r") as f:
        content = f.read()
    logCMD = '''
             var textarea = document.getElementById('logData');
             textarea.scrollTop = textarea.scrollHeight;

             '''
    return content, logCMD

Hope this helps someone else trying to do this. Full physics code is here: https://github.com/plasmapotential/HEAT

1 Like

Woah super cool app @plasmapotential! Would love to hear more about it or see some more screenshots if you feel like sharing in show-and-tell