Search code examples
pythongradio

gradio HTML component with <script> in head of gr.Block not working


I am trying to make a gr.HTML component to be clickable in order to set the display:block of a second gr.HTML component. For this I define a <script> block and add it the the head argument of gr.Block. Unfortunately it does not seem to work. For investigating what happens, I added some debug log messages.

Gradio app:

import gradio as gr

head = """
<script>
  console.log("Script loaded!");
  document.addEventListener("DOMContentLoaded", function() {
      console.log("DOM loaded!");
      var firstDiv = document.getElementById("first_div");
      var secondDiv = document.getElementById("second_div");
      
      if (firstDiv && secondDiv) {
        firstDiv.addEventListener("click", function() {
          console.log("First div clicked!");
          secondDiv.style.display = "block";
        });
      }
  });
</script>
"""

with gr.Blocks(head=head) as demo:
    gr.HTML(
        """
        <div id="first_div" style="cursor:pointer; background-color:#eee; padding:10px; margin-bottom:10px;">
            Click Me!
        </div>
        """
    )
    gr.HTML(
        """
        <div id="second_div" style="display:none; background-color:#ddd; padding:10px;">
            You clicked, I am visible!
        </div>
        """
    )

demo.launch()

I see the log message "Script loaded!" but not "DOM loaded" nor "First div clicked!".

However, when putting the same into a pure HTML as:

<!DOCTYPE html>
<html>
<head>
<script>
  console.log("Scripts loaded!");
  document.addEventListener("DOMContentLoaded", function() {
      console.log("DOM loaded!");
      var firstDiv = document.getElementById("first_div");
      var secondDiv = document.getElementById("second_div");
      
      if (firstDiv && secondDiv) {
        firstDiv.addEventListener("click", function() {
          console.log("First div clicked!");
          secondDiv.style.display = "block";
        });
      }
  });
</script>
</head>
<body>
<div id="first_div" style="cursor:pointer; background-color:#eee; padding:10px; margin-bottom:10px;">
  Click Me!
</div>
<div id="second_div" style="display:none; background-color:#ddd; padding:10px; margin-bottom:10px;">
  You clicked, I am visible
</div>

</body>
</html>

it works as expected.

Also changing the concept from "DOMContentLoaded" to window.onload as:

head = """
<script>
  console.log("Script loaded!");
  window.onload = function() {
    console.log("Window onload fired!");
    const element = document.getElementById("first_div");
    if (element) {
      console.log("Element found!");
    } else {
      console.log("Element not found!");
    };
  };
</script>
"""

or to document.readyState as:

head = """
<script>
  console.log("Script loaded!");
  if (document.readyState === "interactive" || document.readyState === "complete") {
    console.log("Document ready")
    const element = document.getElementById("first_div");
    if (element) {
      console.log("Element found immediately!");
    } else {
      console.log("Element not found immediately!");
    };
  }
</script>
"""

does not help.

What am I missing? Can anybody help, that would absolutely be awesome!


Solution

  • I never used Gradio before, but after looking a bit at the docs I found out a potentially good solution, although with a slightly different approach. The steps are:

    1. Set as head the definition of the function that will be run when the first div is clicked.
    2. Define the HTML elements setting the onclick attribute to the first div, which will be calling the function defined in step 1.

    After running this snippet, it worked for me:

    import gradio as gr
    
    head = """
    <script>
      function clickHandler() {
        console.log("First div clicked!");
        var secondDiv = document.getElementById("second_div");
        secondDiv.style.display = "block";
      }
    </script>
    """
    
    with gr.Blocks(head=head) as demo:
        gr.HTML(
            """
            <div id="first_div" onclick="clickHandler()" style="cursor:pointer; background-color:#eee; padding:10px; margin-bottom:10px;">
                Click Me!
            </div>
            """
        )
        gr.HTML(
            """
            <div id="second_div" style="display:none; background-color:#ddd; padding:10px;">
                You clicked, I am visible!
            </div>
            """
        )
    
    demo.launch()
    

    Hope it works for you, too!