How do I move the default scroll of 'st.text_area' to the bottom?

If you want to scroll to the bottom by default for all text areas on the page, it is a little simpler. If you are trying to selectively choose a particular text area to scroll to the bottom, it is a little trickier. Unfortunately, I’m not aware of a Streamlit version which passes the widget keys forward as an HTML ID that can be used on the front end, so the selection process ends up being a little more convoluted than selecting by ID.

The first thing you’ll need to know is that you need to use the components module to execute JavaScript. Furthermore, when you use the components module, the embedded text will be contained in an iframe so your selector will need to “break out” of that.

The next thing is that javascript will only render the first time it appears on the screen, so if you have a function you want to rerun with each user interaction, you have to do something to make it “look new” to the front end.

Finally, I recommend disabling the text_area element since it appears to be used for display rather than input.

If you don’t have to worry about picking a particular text area, you can do this. This example doesn’t use a form, but it should be easy to add that if you want:

import streamlit as st

if 'chat' not in st.session_state:
    st.session_state.chat = "A: Hello"

def submit():
    st.session_state.chat += f'\nB: {st.session_state.B}'
    st.session_state.chat += '\nA: Some response.'
    # Clear the text input widget for convenience
    st.session_state.B = ''

st.text_area('Chat Log', key='chat', disabled=True)

st.text_input('B\'s Response', key='B', on_change=submit)

# Define the scroll operation as a function and pass in something unique for each
# page load that it needs to re-evaluate where "bottom" is
js = f"""
<script>
    function scroll(dummy_var_to_force_repeat_execution){{
        var textAreas = parent.document.querySelectorAll('.stTextArea textarea');
        for (let index = 0; index < textAreas.length; index++) {{
            textAreas[index].style.color = 'red'
            textAreas[index].scrollTop = textAreas[index].scrollHeight;
        }}
    }}
    scroll({len(st.session_state.chat)})
</script>
"""

st.components.v1.html(js)

If you want to pick and choose a text area to format/affect, you can get it via the label. For “Chat Log” as I’ve used in the above example, that would look like:

parent.document.querySelectorAll('.stTextArea [aria-label="Chat Log"]')
3 Likes