I did something sort of similar. I had a use case where I needed to directly link to another (external) page so it opens in the same top window that the Streamlit app is currently in, and there was no way to do so.
I’m using the combo of st.markdown
and components.html()
to create an invisible link on the topmost browser window, and then I trigger its click
event whenever the user clicks on the visible link inside the standard Streamlit iframe:
st.markdown(f"""
<a id="stripe-btn" href="javascript:void(0);">
<div style="
display: inline-flex;
-webkit-box-align: center;
align-items: center;
-webkit-box-pack: center;
justify-content: center;
font-weight: 400;
padding: 0.25rem 0.75rem;
border-radius: 0.25rem;
margin: 0px;
line-height: 1.6;
width: auto;
user-select: none;
background-color: #FD504D;
color: rgb(255, 255, 255);
border: 1px solid rgb(255, 75, 75);
text-decoration: none;
">
Pay ${price}
</div>
</a>
""", unsafe_allow_html=True)
html(f"""
<script>
function redirectToStripe() {{
window.top.document.getElementById('stripe-link').click();
}}
window.parent.document.getElementById('stripe-btn').addEventListener("click", function(event) {{
redirectToStripe();
event.preventDefault();
}}, false);
// Create iframe element
const redirect_link = document.createElement('a');
redirect_link.href = '{session.url}';
redirect_link.target = '_top';
redirect_link.innerText = 'Invisible Link';
redirect_link.style = 'display:none;';
redirect_link.id = 'stripe-link';
window.top.document.body.appendChild(redirect_link);
</script>
""")