A. 最终效果
需求描述
html javascript css 按年生成2016年至2116年的日历,要求如下:
- 二行六例,每个单元是一个月,且每个单元包含周次信息
- 通过背景为红色的圆圈高亮显示当前的日期
- 第一页显示今年,鼠标左边或键盘左键更新上一年,鼠标右键或键盘右键更新到下一年
- 将html javascript css写到同一个html文件中
- 根据浏览器的宽度,自适应的调整每个月度单元的宽度及字体的大小
- 整个年历在浏览器中水平居中,年份位于日历的正上方
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代码
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)
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 引入外部链接。