Skip to content

Tab-rendering fix (js)

A js fix for certain UI elements, including maps, getting rendered into a zero-sized frame by default. Here we resize it so it is visible once the tab is clicked and no further interaction is required to see it.

inject_iframe_js_code(source)

Injects JavaScript code into a Streamlit app using an iframe.

This function creates a hidden div with a unique ID and injects the provided JavaScript code into the parent document using an iframe. The iframe's source is a JavaScript URL that creates a script element, sets its type to 'text/javascript', and assigns the provided JavaScript code to its text content. The script element is then appended to the hidden div in the parent document.

Parameters:

Name Type Description Default
source str

The JavaScript code to be injected.

required

Returns:

Type Description
None

None

Source code in src/fix_tabrender.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
def inject_iframe_js_code(source: str) -> None:
    """
    Injects JavaScript code into a Streamlit app using an iframe.

    This function creates a hidden div with a unique ID and injects the provided
    JavaScript code into the parent document using an iframe. The iframe's source
    is a JavaScript URL that creates a script element, sets its type to 'text/javascript',
    and assigns the provided JavaScript code to its text content. The script element
    is then appended to the hidden div in the parent document.

    Args:
        source (str): The JavaScript code to be injected.

    Returns:
        None
    """
    div_id = uuid.uuid4()

    st.markdown(
        f"""
    <div style="height: 0; width: 0; overflow: hidden;" 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); \
            setTimeout(function() {{ }}, 0); \
        "></iframe>
    </div>
    """,
        unsafe_allow_html=True,
    )

js_show_zeroheight_iframe(component_iframe_title, height='auto')

Injects JavaScript code to dynamically set iframe height (located by title)

This function generates and injects JavaScript code that searches for iframes with the given title and sets their height to the specified value. The script attempts to find the iframes up to a maximum number of attempts, and also listens for user interactions to reattempt setting the height.

See https://github.com/streamlit/streamlit/issues/7376

Parameters:

Name Type Description Default
component_iframe_title str

The title attribute of the iframes to target.

required
height str

The height to set for the iframes. Defaults to "auto".

'auto'
Notes
  • The JavaScript code will attempt to find the iframes every 250 milliseconds, up to a maximum of 20 attempts.
  • If the iframes are found, their height will be set to the specified value.
  • User interactions (e.g., click events) triggers a reattempt to set the height.
Source code in src/fix_tabrender.py
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
def js_show_zeroheight_iframe(component_iframe_title: str, height: str = "auto") -> None:
    """
    Injects JavaScript code to dynamically set iframe height (located by title)

    This function generates and injects JavaScript code that searches for
    iframes with the given title and sets their height to the specified value.
    The script attempts to find the iframes up to a maximum number of attempts,
    and also listens for user interactions to reattempt setting the height.

    See https://github.com/streamlit/streamlit/issues/7376 


    Args:
        component_iframe_title (str): The title attribute of the iframes to target.
        height (str, optional): The height to set for the iframes. Defaults to "auto".

    Notes:
        - The JavaScript code will attempt to find the iframes every 250
          milliseconds, up to a maximum of 20 attempts.
        - If the iframes are found, their height will be set to the specified value.
        - User interactions (e.g., click events) triggers a reattempt to set the height.
    """
    source = f"""
    (function() {{
    var attempts = 0;
    const maxAttempts = 20; // Max attempts to find the iframe
    const intervalMs = 250; // Interval between attempts in milliseconds

    function setIframeHeight() {{
        const intervalId = setInterval(function() {{
            var iframes = document.querySelectorAll('iframe[title="{component_iframe_title}"]');
            if (iframes.length > 0 || attempts > maxAttempts) {{
                if (iframes.length > 0) {{
                    iframes.forEach(iframe => {{
                        if (iframe || iframe.height === "0" || iframe.style.height === "0px") {{
                            iframe.style.height = "{height}";
                            iframe.setAttribute("height", "{height}");
                            console.log('Height of iframe with title "{component_iframe_title}" set to {height}.');
                        }}
                    }});
                }} else {{
                    console.log('Iframes with title "{component_iframe_title}" not found after ' + maxAttempts + ' attempts.');
                }}
                clearInterval(intervalId); // Stop checking
            }}
            attempts++;
        }}, intervalMs);
    }}


    function trackInteraction(event) {{
        console.log('User interaction detected:', event.type);
        setIframeHeight();
    }}

    setIframeHeight();
    document.addEventListener('click', trackInteraction);
}})();
    """
    inject_iframe_js_code(source)