如何用JS更新Streamlit元素?扩展器自定义ID遇加载问题
为Streamlit Expander添加自定义ID的问题
我正在开发一个包含多个Expander的Streamlit应用,出于测试需求需要给每个容器加可识别的ID标签。完全不懂JavaScript,试了下面的实现,但发现JS脚本运行时Streamlit的内容还没加载,日志显示body元素是空的,该怎么解决?
我的尝试
import streamlit as st import pandas as pd import streamlit.components.v1 as components # 创建示例数据框 data1 = {'Column 1': [1, 2, 3, 4], 'Column 2': [10, 20, 30, 40]} data2 = {'Column A': ['A', 'B', 'C', 'D'], 'Column B': ['W', 'X', 'Y', 'Z']} data3 = {'Column X': [100, 200, 300, 400], 'Column Y': [1000, 2000, 3000, 4000]} df1 = pd.DataFrame(data1) df2 = pd.DataFrame(data2) df3 = pd.DataFrame(data3) # 定义带指定ID的Expander列表 expanders = [ {"title": "Expander 1", "dataframe": df1, "id": "expander-unique-id-1"}, {"title": "Expander 2", "dataframe": df2, "id": "expander-unique-id-2"}, {"title": "Expander 3", "dataframe": df3, "id": "expander-unique-id-3"}, ] for expander in expanders: with st.expander(expander["title"], expanded=True): st.dataframe(expander["dataframe"]) # 注入JavaScript为每个Expander设置自定义ID components.html(f""" <script> // 等待Streamlit加载Expander元素 window.addEventListener("load", function(){{setTimeout(assignIds, 3000)}}); function assignIds() {{ const expanders = {len(expanders)}; const ids = {str([exp["id"] for exp in expanders])}; var expander_list = document.querySelectorAll('stExpander'); console.log(document.querySelectorAll('html')) for (let i = 0; i < expanders; i++) {{ const expander = expander_list[i]; if (expander) {{ expander.id = ids[i]; console.log("已设置唯一ID的Expander:", expander); }} }} }} </script> """, height=0) st.write("已为Expander添加唯一ID用于追踪。")
解决方法
问题出在Streamlit是动态渲染组件的,window.load事件触发时,Expander还没被渲染到DOM中,固定延迟也不可靠(加载速度波动会导致依然取不到元素)。改用MutationObserver监听DOM变化,直到所有目标Expander都加载完成再设置ID,这是更可靠的方式。
修改后的完整代码:
import streamlit as st import pandas as pd import streamlit.components.v1 as components # 创建示例数据框 data1 = {'Column 1': [1, 2, 3, 4], 'Column 2': [10, 20, 30, 40]} data2 = {'Column A': ['A', 'B', 'C', 'D'], 'Column B': ['W', 'X', 'Y', 'Z']} data3 = {'Column X': [100, 200, 300, 400], 'Column Y': [1000, 2000, 3000, 4000]} df1 = pd.DataFrame(data1) df2 = pd.DataFrame(data2) df3 = pd.DataFrame(data3) # 定义带指定ID的Expander列表 expanders = [ {"title": "Expander 1", "dataframe": df1, "id": "expander-unique-id-1"}, {"title": "Expander 2", "dataframe": df2, "id": "expander-unique-id-2"}, {"title": "Expander 3", "dataframe": df3, "id": "expander-unique-id-3"}, ] for expander in expanders: with st.expander(expander["title"], expanded=True): st.dataframe(expander["dataframe"]) # 注入改进后的JavaScript components.html(f""" <script> const targetCount = {len(expanders)}; const targetIds = {str([exp["id"] for exp in expanders])}; // 创建观察者监听DOM变化 const observer = new MutationObserver((mutations) => {{ const expanderElements = document.querySelectorAll('stExpander'); // 检查是否所有目标Expander都已加载 if (expanderElements.length === targetCount) {{ expanderElements.forEach((el, index) => {{ el.id = targetIds[index]; console.log("已设置ID:", el.id); }}); // 完成后停止监听 observer.disconnect(); }} }}); // 监听body的所有子元素变化 observer.observe(document.body, {{ childList: true, subtree: true }}); </script> """, height=0) st.write("已为Expander添加唯一ID用于追踪。")
关键说明
- MutationObserver:可以实时监听DOM的变化,当Streamlit动态渲染出Expander元素时,能立刻检测到
- 监听范围设置为
document.body并开启subtree: true,确保能捕获到所有嵌套渲染的元素 - 当检测到的Expander数量和目标数量一致时,设置ID并停止监听,避免重复执行
内容的提问来源于stack exchange,提问作者anmol_gorakshakar
相关产品推荐
相关产品推荐

