Why can I not run JavaScript using components.html?

I am trying to run a simple code containing JavaScript. The code is a contact form initialized from a html-file. I am trying to set a placeholder to email input using javascript, but nothing happens (even the alert or the console.log runs) .
But if I run the code inside the html file within a script tag, the code runs fine.
This is my code:

from streamlit.components.v1 import html

form_from_html = "html/myForm.html"

with open(form_from_html, 'r') as f:
    html_form = f.read()

html(html_form, height=600, width=490)

js_test = """
            <script language="javascript">
                const cf = document.forms.contactForm;
                cf.elements.email.placeholder = "test@example.com";
                alert("this is a test!");
                console.log("test");
             </script>
          """
html(js_test)

I am wondering why the code (alert and console.log) runs fine when I remove:
const cf = document.forms.contactForm; and
cf.elements.email.placeholder = "test@example.com";

I tried to put the script in a javascript file and using it as a script tag into the html file. But it doesn’t work rather. I am getting:
Loading failed for the with source “http://localhost:8501/js/test.js” (even from safe mode firefox, edge or chrome)

Any help or suggestion is very appreciated.

Hi @karl16A,

The problem is that each html() is in a separate iframe, so the javascript in your second html cannot access the forms and elements of the first one. You will need to add the script into the myForm.html, not using a script tag to reference an external file, but just like you have it here, as an inline script tag.

e.g.

from streamlit.components.v1 import html

html_form = """
<form method="post" name="contactForm">
    <input type="email" name="email" placeholder="Email" required>
</form>
"""
js_test = """
            <script language="javascript">
                const cf = document.forms.contactForm;
                console.log(document.forms);
                cf.elements.email.placeholder = "test@example.com";
                alert("this is a test!");
                console.log("test");
             </script>
          """
html(html_form + js_test)

Hi @ blackary,
Sorry for the late reply and thanks for your reply. It helped me to understand the issue.
But is it possible to add/change something in the first html() later in the code, like a button.click() (I understand now the iframe concept)? or I have to put everything that will happen to my form in the first and only html()?

@karl16A Good question – if your app looks like this:

Manipulating the html string after you add it to the page will not affect what has already been sent. By default, streamlit apps always run from top to bottom, and so something you do after a line won’t affect the previous line. You may be able to figure out a way around this by storing your HTML string in st.session_state, and using st.experimental_rerun to run the script again after you’ve edited it, but the most straightforward method is to do all your edits to the HTML string before you add it to the page, if possible.

1 Like

@blackary Thanks again.