JS and CSS working in HTML but not in streamlit app

Summary

I have some CSS and Javascript code that works correctly when deployed in a HTML file, but when copied and pasted into my streamlit app it does not work properly.

Steps to reproduce

Code snippet:

if st.button('Video'):


    with open("/users/jaredperez/documents/local_testing/output.mp4", "rb") as f:
        video_bytes = f.read()
        video_str = "data:video/mp4;base64,%s" % base64.b64encode(video_bytes).decode()
        video_html = """
            <video id="myvideo" width="700" controls autoplay>
                <source src="%s" type="video/mp4">
                Your browser does not support HTML5 video.
            </video>
        """ % video_str
        

    with open('/users/jaredperez/documents/local_testing/rain-01.mp3', 'rb') as f:
        audio_bytes = f.read()
        audio_str = "data:audio/ogg;base64,%s" % base64.b64encode(audio_bytes).decode()
        audio_html = """
                    <audio id="myaudio" autoplay class="stAudio">
                    <source src="%s" type="audio/ogg">
                    Your browser does not support the audio element.
                    </audio>
                """ % audio_str 


    # # Define the CSS for the video and audio elements */
    css="""
    myvideo {
        position: absolute;
        top: 0;
        left: 0;
    }
    
    myaudio {
        position: absolute;
        top: 0;
        left: 0;
        opacity: 0;
    }
    
    myvideo:paused,
    myaudio:paused {
    opacity: 0;
    }

    
    """

    
    # Define the JavaScript to play the video and audio simultaneously */
    js="""
    const video = document.getElementById('myvideo');
    const audio = document.getElementById('myaudio');

    
    video.addEventListener('play', () => {
        audio.currentTime = video.currentTime;
        audio.play();
    });
    
    video.addEventListener('pause', () => {
        audio.pause();
    });
    
    video.addEventListener('seeked', () => {
        audio.currentTime = video.currentTime;
    }); 
    
    video.addEventListener('ended', () => {
    audio.pause();
    });
    
    """
    # Render the video and audio elements, and the CSS and JavaScript

    video_placeholder = st.empty()
    video_placeholder.write(video_html, unsafe_allow_html=True)
    audio_placeholder = st.empty()
    audio_placeholder.write(audio_html, unsafe_allow_html=True)
    st.markdown(f'<script>{js}</script>', unsafe_allow_html=True)
    st.markdown(f'<style>{css}</style>', unsafe_allow_html=True)

The functioning HTML looks like

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>My Page</title>
  </head>
  <body>
    <video id="myvideo" width="700" controls autoplay>
      <source src="/users/jaredperez/documents/local_testing/output.mp4" type="video/mp4">
      Your browser does not support HTML5 video.
    </video>

    <audio id="myaudio" class="stAudio" autoplay>
      <source src="/users/jaredperez/documents/local_testing/rain-01.mp3" type="audio/mpeg">
      Your browser does not support the audio element.
    </audio>

    <script>
      const video = document.getElementById('myvideo');
      const audio = document.getElementById('myaudio');

      video.addEventListener('play', () => {
          audio.currentTime = video.currentTime;
          audio.play();
      });

      video.addEventListener('pause', () => {
          audio.pause();
      });

      video.addEventListener('seeked', () => {
          audio.currentTime = video.currentTime;
      }); 

      video.addEventListener('ended', () => {
        audio.pause();
      });
    </script>
  </body>
</html>

If applicable, please provide the steps we should take to reproduce the error or specified behavior.

The goal is for when I pause the video, the overlaid audio also pauses. This action occurs in my HTML file when I copy and paste the CSS and Java but does not occur in streamlit.

Explain what you expect to happen when you run the code above.

When I pause the video in streamlit the audio continues to run, and when the video ends in streamlit the audio continues to run.

You can’t run JavaScript through st.markdown, you need the components API.

@Jared_Perez Hi, JS is not directly supported in streamlit.
As @mathcatsand mentioned, give a try in streamlit components which is available in below url

steps are self explanatory in above document to achieve what are you trying

I am not entirely sure what I am doing wrong (I am new to JS/HTML etc.) but i essentially copied and pasted the example from the documentation but replaced it with my own HTML and nothing renders. Any advice?

import streamlit as st
import streamlit.components.v1 as components


components.html(
    """
    <html>
    <head>
      <meta charset="UTF-8">
      <title>My Page</title>
    </head>
    <body>
      <video id="myvideo" width="700" controls autoplay>
        <source src="file:///users/jaredperez/documents/local_testing/output.mp4" type="video/mp4">
        Your browser does not support HTML5 video.
      </video>

      <audio id="myaudio" class="stAudio" autoplay>
        <source src="file:///users/jaredperez/documents/local_testing/rain-01.mp3" type="audio/mpeg">
        Your browser does not support the audio element.
      </audio>

      <script>
        const video = document.getElementById('myvideo');
        const audio = document.getElementById('myaudio');

        video.addEventListener('play', () => {
            audio.currentTime = video.currentTime;
            audio.play();
        });

        video.addEventListener('pause', () => {
            audio.pause();
        });

        video.addEventListener('seeked', () => {
            audio.currentTime = video.currentTime;
        }); 

        video.addEventListener('ended', () => {
          audio.pause();
        });
      </script>
    </body>
  </html>
    """,
    height=600,
)

First, note that components.html already wraps your content inside a <body> tag within an <iframe>, so you don’t need the extra layers.

I’m not sure what your local environment is exactly, but I suspect an issue with your file locations. Try turning on static hosting and moving your videos to the static hosting folder so you can point to them via url.

I was able to solve it by replacing document.get with window.parent.document.get in the lines as you suggested in your other post and using the components API. Thank you! :slight_smile:

const video = document.getElementById('myvideo');
const audio = document.getElementById('myaudio');
const video = window.parent.document.getElementById('myvideo');
const audio = window.parent.document.getElementById('myaudio');
2 Likes

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.