A. 最终效果

需求描述

html javascript css 按年生成2016年至2116年的日历,要求如下:

  1. 二行六例,每个单元是一个月,且每个单元包含周次信息
  2. 通过背景为红色的圆圈高亮显示当前的日期
  3. 第一页显示今年,鼠标左边或键盘左键更新上一年,鼠标右键或键盘右键更新到下一年
  4. 将html javascript css写到同一个html文件中
  5. 根据浏览器的宽度,自适应的调整每个月度单元的宽度及字体的大小
  6. 整个年历在浏览器中水平居中,年份位于日历的正上方

240828-Gradio结合Html+Css+Javascript制作年历_javascript


B. HTML代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Yearly Calendar</title>
    <style>
      body {
        display: flex;
        flex-direction: column;
        align-items: center;
        font-family: Arial, sans-serif;
        margin: 0;
        padding: 0;
      }
      #year {
        margin: 20px 0;
        font-size: 2rem;
        text-align: center;
      }
      .calendar {
        display: grid;
        grid-template-columns: repeat(6, 1fr);
        gap: 10px;
        width: 90%;
        max-width: 1200px;
      }
      .month {
        border: 1px solid #ddd;
        padding: 10px;
        box-sizing: border-box;
      }
      .month h3 {
        margin: 0;
        font-size: 1.2rem;
        text-align: center;
      }
      .weekdays,
      .days {
        display: grid;
        grid-template-columns: repeat(7, 1fr);
        text-align: center;
        font-size: 0.9rem;
      }
      .weekdays div {
        font-weight: bold;
        padding: 5px 0;
      }
      .days div {
        padding: 5px;
        cursor: pointer;
      }
      .days .today {
        background-color: red;
        border-radius: 50%;
        color: white;
      }
      @media (max-width: 800px) {
        .calendar {
          grid-template-columns: repeat(3, 1fr);
        }
        .month h3 {
          font-size: 1rem;
        }
        .weekdays,
        .days {
          font-size: 0.8rem;
        }
      }
      @media (max-width: 500px) {
        .calendar {
          grid-template-columns: repeat(2, 1fr);
        }
        .month h3 {
          font-size: 0.9rem;
        }
        .weekdays,
        .days {
          font-size: 0.7rem;
        }
      }
    </style>
  </head>
  <body>
    <div id="year"></div>
    <div class="calendar" id="calendar"></div>
    <script>
      const monthNames = [
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December",
      ];
      const weekdays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
      let currentYear = new Date().getFullYear();

      document.addEventListener("DOMContentLoaded", () => {
        renderCalendar(currentYear);
        document.getElementById("year").innerText = currentYear;
      });

      document.addEventListener("keydown", (event) => {
        if (event.key === "ArrowLeft") {
          updateYear(-1);
        } else if (event.key === "ArrowRight") {
          updateYear(1);
        }
      });

      document.addEventListener("click", (event) => {
        if (event.button === 0) {
          // Left click
          updateYear(-1);
        }
      });

      document.addEventListener("contextmenu", (event) => {
        event.preventDefault(); // Prevent default context menu
        updateYear(1); // Right click
      });

      function updateYear(offset) {
        currentYear += offset;
        if (currentYear < 2016) currentYear = 2016;
        if (currentYear > 2116) currentYear = 2116;
        document.getElementById("year").innerText = currentYear;
        renderCalendar(currentYear);
      }

      function renderCalendar(year) {
        const calendar = document.getElementById("calendar");
        calendar.innerHTML = "";
        for (let i = 0; i < 12; i++) {
          const monthDiv = document.createElement("div");
          monthDiv.className = "month";
          const monthTitle = document.createElement("h3");
          monthTitle.innerText = monthNames[i];
          monthDiv.appendChild(monthTitle);

          const weekdaysDiv = document.createElement("div");
          weekdaysDiv.className = "weekdays";
          weekdays.forEach((day) => {
            const dayDiv = document.createElement("div");
            dayDiv.innerText = day;
            weekdaysDiv.appendChild(dayDiv);
          });
          monthDiv.appendChild(weekdaysDiv);

          const daysDiv = document.createElement("div");
          daysDiv.className = "days";
          const firstDay = new Date(year, i, 1).getDay();
          const daysInMonth = new Date(year, i + 1, 0).getDate();

          // Add empty divs for days before the first day of the month
          for (let j = 0; j < firstDay; j++) {
            daysDiv.appendChild(document.createElement("div"));
          }

          // Add days of the month
          for (let j = 1; j <= daysInMonth; j++) {
            const dayDiv = document.createElement("div");
            dayDiv.innerText = j;
            const today = new Date();
            if (
              year === today.getFullYear() &&
              i === today.getMonth() &&
              j === today.getDate()
            ) {
              dayDiv.className = "today";
            }
            daysDiv.appendChild(dayDiv);
          }

          monthDiv.appendChild(daysDiv);
          calendar.appendChild(monthDiv);
        }
      }
    </script>
  </body>
</html>

C. Gradio代码

C.1 没调整垂直组件之间的间距

import gradio as gr

html_iframe = """
<iframe srcdoc='
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Yearly Calendar</title>
    <style>
      body {
        display: flex;
        flex-direction: column;
        align-items: center;
        font-family: Arial, sans-serif;
        margin: 0;
        padding: 0;
      }
      #year {
        margin: 20px 0;
        font-size: 2rem;
        text-align: center;
      }
      .calendar {
        display: grid;
        grid-template-columns: repeat(6, 1fr);
        gap: 10px;
        width: 90%;
        max-width: 1200px;
      }
      .month {
        border: 1px solid #ddd;
        padding: 10px;
        box-sizing: border-box;
      }
      .month h3 {
        margin: 0;
        font-size: 1.2rem;
        text-align: center;
      }
      .weekdays,
      .days {
        display: grid;
        grid-template-columns: repeat(7, 1fr);
        text-align: center;
        font-size: 0.9rem;
      }
      .weekdays div {
        font-weight: bold;
        padding: 5px 0;
      }
      .days div {
        padding: 5px;
        cursor: pointer;
      }
      .days .today {
        background-color: red;
        border-radius: 50%;
        color: white;
      }
      @media (max-width: 800px) {
        .calendar {
          grid-template-columns: repeat(3, 1fr);
        }
        .month h3 {
          font-size: 1rem;
        }
        .weekdays,
        .days {
          font-size: 0.8rem;
        }
      }
      @media (max-width: 500px) {
        .calendar {
          grid-template-columns: repeat(2, 1fr);
        }
        .month h3 {
          font-size: 0.9rem;
        }
        .weekdays,
        .days {
          font-size: 0.7rem;
        }
      }
    </style>
  </head>
  <body>
    <div id="year"></div>
    <div class="calendar" id="calendar"></div>
    <script>
      const monthNames = [
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December",
      ];
      const weekdays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
      let currentYear = new Date().getFullYear();

      document.addEventListener("DOMContentLoaded", () => {
        renderCalendar(currentYear);
        document.getElementById("year").innerText = currentYear;
      });

      document.addEventListener("keydown", (event) => {
        if (event.key === "ArrowLeft") {
          updateYear(-1);
        } else if (event.key === "ArrowRight") {
          updateYear(1);
        }
      });

      document.addEventListener("click", (event) => {
        if (event.button === 0) {
          // Left click
          updateYear(-1);
        }
      });

      document.addEventListener("contextmenu", (event) => {
        event.preventDefault(); // Prevent default context menu
        updateYear(1); // Right click
      });

      function updateYear(offset) {
        currentYear += offset;
        if (currentYear < 2016) currentYear = 2016;
        if (currentYear > 2116) currentYear = 2116;
        document.getElementById("year").innerText = currentYear;
        renderCalendar(currentYear);
      }

      function renderCalendar(year) {
        const calendar = document.getElementById("calendar");
        calendar.innerHTML = "";
        for (let i = 0; i < 12; i++) {
          const monthDiv = document.createElement("div");
          monthDiv.className = "month";
          const monthTitle = document.createElement("h3");
          monthTitle.innerText = monthNames[i];
          monthDiv.appendChild(monthTitle);

          const weekdaysDiv = document.createElement("div");
          weekdaysDiv.className = "weekdays";
          weekdays.forEach((day) => {
            const dayDiv = document.createElement("div");
            dayDiv.innerText = day;
            weekdaysDiv.appendChild(dayDiv);
          });
          monthDiv.appendChild(weekdaysDiv);

          const daysDiv = document.createElement("div");
          daysDiv.className = "days";
          const firstDay = new Date(year, i, 1).getDay();
          const daysInMonth = new Date(year, i + 1, 0).getDate();

          // Add empty divs for days before the first day of the month
          for (let j = 0; j < firstDay; j++) {
            daysDiv.appendChild(document.createElement("div"));
          }

          // Add days of the month
          for (let j = 1; j <= daysInMonth; j++) {
            const dayDiv = document.createElement("div");
            dayDiv.innerText = j;
            const today = new Date();
            if (
              year === today.getFullYear() &&
              i === today.getMonth() &&
              j === today.getDate()
            ) {
              dayDiv.className = "today";
            }
            daysDiv.appendChild(dayDiv);
          }

          monthDiv.appendChild(daysDiv);
          calendar.appendChild(monthDiv);
        }
      }
    </script>
  </body>
</html>' width="100%" height="800px" style="border:none;"></iframe>
"""

with gr.Blocks() as demo:
    gr.HTML(html_iframe)

demo.launch(inbrowser=True)

C.2 调整垂直组件之间的间距

import gradio as gr

html_iframe = """
<iframe srcdoc='
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Yearly Calendar</title>
    <style>
      body {
        display: flex;
        flex-direction: column;
        align-items: center;
        font-family: Arial, sans-serif;
        margin: 0;
        padding: 0;
      }
      #year {
        margin: 20px 0;
        font-size: 2rem;
        text-align: center;
      }
      .calendar {
        display: grid;
        grid-template-columns: repeat(6, 1fr);
        gap: 10px;
        width: 90%;
        max-width: 1200px;
      }
      .month {
        border: 1px solid #ddd;
        padding: 10px;
        box-sizing: border-box;
      }
      .month h3 {
        margin: 0;
        font-size: 1.2rem;
        text-align: center;
      }
      .weekdays,
      .days {
        display: grid;
        grid-template-columns: repeat(7, 1fr);
        text-align: center;
        font-size: 0.9rem;
      }
      .weekdays div {
        font-weight: bold;
        padding: 5px 0;
      }
      .days div {
        padding: 5px;
        cursor: pointer;
      }
      .days .today {
        background-color: red;
        border-radius: 50%;
        color: white;
      }
      @media (max-width: 800px) {
        .calendar {
          grid-template-columns: repeat(3, 1fr);
        }
        .month h3 {
          font-size: 1rem;
        }
        .weekdays,
        .days {
          font-size: 0.8rem;
        }
      }
      @media (max-width: 500px) {
        .calendar {
          grid-template-columns: repeat(2, 1fr);
        }
        .month h3 {
          font-size: 0.9rem;
        }
        .weekdays,
        .days {
          font-size: 0.7rem;
        }
      }
    </style>
  </head>
  <body>
    <div id="year"></div>
    <div class="calendar" id="calendar"></div>
    <script>
      const monthNames = [
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December",
      ];
      const weekdays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
      let currentYear = new Date().getFullYear();

      document.addEventListener("DOMContentLoaded", () => {
        renderCalendar(currentYear);
        document.getElementById("year").innerText = currentYear;
      });

      document.addEventListener("keydown", (event) => {
        if (event.key === "ArrowLeft") {
          updateYear(-1);
        } else if (event.key === "ArrowRight") {
          updateYear(1);
        }
      });

      document.addEventListener("click", (event) => {
        if (event.button === 0) {
          // Left click
          updateYear(-1);
        }
      });

      document.addEventListener("contextmenu", (event) => {
        event.preventDefault(); // Prevent default context menu
        updateYear(1); // Right click
      });

      function updateYear(offset) {
        currentYear += offset;
        if (currentYear < 2016) currentYear = 2016;
        if (currentYear > 2116) currentYear = 2116;
        document.getElementById("year").innerText = currentYear;
        renderCalendar(currentYear);
      }

      function renderCalendar(year) {
        const calendar = document.getElementById("calendar");
        calendar.innerHTML = "";
        for (let i = 0; i < 12; i++) {
          const monthDiv = document.createElement("div");
          monthDiv.className = "month";
          const monthTitle = document.createElement("h3");
          monthTitle.innerText = monthNames[i];
          monthDiv.appendChild(monthTitle);

          const weekdaysDiv = document.createElement("div");
          weekdaysDiv.className = "weekdays";
          weekdays.forEach((day) => {
            const dayDiv = document.createElement("div");
            dayDiv.innerText = day;
            weekdaysDiv.appendChild(dayDiv);
          });
          monthDiv.appendChild(weekdaysDiv);

          const daysDiv = document.createElement("div");
          daysDiv.className = "days";
          const firstDay = new Date(year, i, 1).getDay();
          const daysInMonth = new Date(year, i + 1, 0).getDate();

          // Add empty divs for days before the first day of the month
          for (let j = 0; j < firstDay; j++) {
            daysDiv.appendChild(document.createElement("div"));
          }

          // Add days of the month
          for (let j = 1; j <= daysInMonth; j++) {
            const dayDiv = document.createElement("div");
            dayDiv.innerText = j;
            const today = new Date();
            if (
              year === today.getFullYear() &&
              i === today.getMonth() &&
              j === today.getDate()
            ) {
              dayDiv.className = "today";
            }
            daysDiv.appendChild(dayDiv);
          }

          monthDiv.appendChild(daysDiv);
          calendar.appendChild(monthDiv);
        }
      }
    </script>
  </body>
</html>' width="100%" height="800px" style="border:none;"></iframe>
"""



css = """
#warning {background-color: #C0C0C0}
.feedback textarea {font-size: 24px !important}

#element {
    //border: 1px solid black; /* 1px 宽度,实线,黑色 */
    margin-bottom: -250px; /* 使用负的外边距值 */
}

"""

with gr.Blocks(css=css) as demo:
    gr.HTML(html_iframe, elem_id="element")
    gr.Textbox(label='info', elem_id="warning", elem_classes="feedback")

demo.launch(inbrowser=True)

D. 问题分析

问题描述:

直接将html文件中的内容插入到gr.HTML()后无法显示

解决方案:

代码中没有任何显示的原因可能是由于 Gradio 的 gr.HTML 组件在显示 HTML 内容时的限制问题。gr.HTML 组件只能渲染纯 HTML 和 CSS 内容,对于包含 JavaScript 的代码,它不会自动执行脚本。因此,尽管 HTML 内容被正确加载,JavaScript 却没有执行,从而导致页面没有预期的动态效果。

为了在 Gradio 中正确展示和运行 JavaScript,可以尝试以下解决方案:

使用 IFrame: 将完整的 HTML 内容放入一个独立的 HTML 文件中,然后通过 Gradio 的 gr.HTML 加载一个 IFrame,将这个 HTML 文件嵌入其中。
托管网页并嵌入: 将 HTML 内容托管在一个 web 服务器上(例如 GitHub Pages),然后在 Gradio 中通过 IFrame 引入外部链接。