Injecting JS?

Hi, i have a short line of JS that i want to inject into the html page.
I am able to easely inject the HTML inside, but for some reason the JS part is not showing:

this is what im injecting:

<button id="close_error" type="button" class="close-error-btn" onclick="document.getElementById('loader-block').style.display = 'none'">
        X
</button>

this is what im getting in the DOM:

<button id="close_error" type="button" class="close-error-btn">
        X
  </button>

as you can see the onclick is dissapearing

I also tried using it just as a script, and it does show, but does not work, meaning that on click does nothing…

is there a way to inject JS?

Hi @BugzTheBunny !

Can you share a bit more of the code, like how do you inject the code and what the onclick loader-block ID is supposed to be?
If you are using st.markdown with unsafe_allow_html then all Javascript is unfortunately being stripped away.

  • Natively, you’ll have to use components.html, and you onclick behavior will probably need to leave the iframe to impact Streamlit components on the page, like showcased here
  • If doing it the native way is a bit too cumbersome, @Probability 's hydralit-components also has an experimental feature for injecting HTML/JS you should try, but you’ll probably get the same iframe problem if your loader-block ID is outside the component iframe.

So I guess the final answer would depend on like the rest of the code :slight_smile:

Have a nice day :balloon:
Fanilo

1 Like

Hey,

Yeah, unfortunatly the only options are to actually inject iframes, which for some reason do not work, i tried to work it around with parent iframe, but it also does not work…

I got a custom message component that runs on error, kind of a wrapper.
I want to add a close button to it, to hide it on click.

looks like this :

    error = rf'''
            <div class="container loader-block" id="loader-block">
                <h1>
                    {title}
                </h1>
                <h2>
                    {subtitle}
                </h2>
                <img src="data:image/png;base64, {image_as_base64.decode("utf-8")}"/>
                <div class="error-box">
                   {utl.generate_download(error_content,"error.txt","Error log")}
                </div>
                <button id="close_error" type="button" class="close-error-btn">
                    X
                </button>
            </div>
            '''
    return st.markdown(error,unsafe_allow_html=True)

now i want to attach to it close functionality using JS, but i tried everything i could find, and nothing works, Iframes are also not working, because of cross origin…

Not sure if it solves your issue, but maybe you have to wait until the page is loaded before you can access the element:

I also wrote a custom component to run javascript: Streamlit-javascript, run client side javascript and get result - #2 by andfanilo

2 Likes

Hello @BugzTheBunny, maybe the following snippet could help you solve your cross-origin issue:

import html
import streamlit as st
import uuid


def javascript(source: str) -> None:
    div_id = uuid.uuid4()

    st.markdown(f"""
    <div style="display:none" id="{div_id}">
        <iframe src="javascript: \
            var script = document.createElement('script'); \
            script.type = 'text/javascript'; \
            script.text = {html.escape(repr(source))}; \
            var div = window.parent.document.getElementById('{div_id}'); \
            div.appendChild(script); \
            div.parentElement.parentElement.parentElement.style.display = 'none'; \
        "/>
    </div>
    """, unsafe_allow_html=True)

1 Like

Solved it!! Muahahaha

So, i have this component, this component popups when im getting an error (custom made)
image

I wanted to make the button (x button) close this pop up.

So this is my solution:

    error = rf'''
            <div class="container loader-block" id="loader-block">
                <h1>
                    {title}
                </h1>
                <h2>
                    {subtitle}
                </h2>
                <img src="data:image/png;base64, {image_as_base64.decode("utf-8")}"/>
                <div class="error-box">
                   {utl.generate_download(error_content,"error.txt","Error log")}
                </div>
                <button id="close_error" type="button" class="close-error-btn">
                    X
                </button>
            </div>
            '''
    st.markdown(error,unsafe_allow_html=True)
    js = '''<script>
    close_btn = window.parent.document.getElementById("close_error").addEventListener("click", () => {
        error_box = window.parent.document.getElementById("loader-block"); 
        error_box.style.display = "none";
        });
    </script>
    '''
    html(js)

How it works?
The JS is loaded inside an iframe as you all know I assume (the html method).
The js searched for the parent item in the page (the one where the iframe is aka the one where the component is) and then the JS adds a listener to the element.

1 Like

Thanks!

Solved it yesterday in a similar way, added the solution.

Noice, will be waiting for the shared app :upside_down_face:

1 Like

Will create a short demo app later today, with some CSS, to make it usefull, will update my last repository.

Looking fwd to the demo :wink: