基于Flexbox与JavaScript的Etch-a-Sketch网格容器尺寸问题
Etch-a-Sketch 项目问题与解决方案
我正在完成The Odin Project的Etch-a-Sketch项目,希望实现超出作业要求的功能,且仅使用Flexbox和JavaScript(不使用CSS Grid)。遇到以下两个问题:
- 当设置
gridContainer.style.height = "100vh"时,容器高度略超出屏幕范围,目前使用97vh替代,是否存在更优解决方案? - 希望仅在移动端屏幕下将容器设置为
width=100vw(替换原97vh的高度设置),但尝试CSS媒体查询和JavaScript媒体查询后,容器高度仍大于宽度,无法保持正方形形态,请问该如何实现?
相关代码
HTML代码
<!DOCTYPE html> <html lang="en"> <head> <link rel="stylesheet" href="./styles.css" /> <script src="./script.js" defer></script> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Etch-a-Sketch</title> </head> <body> <div id="gridUser"> <h1>Etch-a-Sketch</h1> <form id="gridForm"> <p><label for="gridInput">Enter a number of squares per side: 1...100</label></p> <p><input id="gridInput" type="text"></p> <p><legend>Choose mouse hover color</legend></p> <p> <input id="gridRadioYellow" name="gridRadio" value ="yellow" type="radio" checked> <label for="gridRadioYellow"> Yellow</label> <input id="gridRadioRandomize" name="gridRadio" value ="randomize" type="radio"> <label for="gridRadioRandomize"> Randomize</label> <input id="gridRadioRandomizeEverySquare" name="gridRadio" value ="randomizeEverySquare" type="radio"> <label for="gridRadioRandomizeEverySquare"> Randomize every square</label> </p> <p><button type="submit">Submit</button></p> </form> <div id="gridText"></div><br/> </div> <div id="gridContainer"></div> </body> </html>
CSS代码
* { box-sizing: border-box; font-family: Arial, sans-serif; text-align: center; } body { display: flex; flex-flow: row wrap; justify-content: flex-start; } #gridUser, #gridContainer { flex: 1 1 0%; } #gridInput { text-align: left; }
JavaScript代码
// also add functional for yellow color (not randomize) and for randomize every square let gridContainer = document.querySelector("#gridContainer"); let gridSize = 16*16; let gridSquares = ""; let mouseSquares = []; let mouseHoveringColor = ""; getGridPerSide(); function getGridPerSide() { let gridForm = document.querySelector("#gridForm"); gridForm.addEventListener("submit", (e) => { e.preventDefault(); let gridInput = document.querySelector("#gridInput"); let gridRadio = document.querySelectorAll(`input[name="gridRadio"]`); let gridRadioChecked = ""; let gridText = document.querySelector("#gridText"); mouseHoveringColor = ""; for (const gridRadioOne of gridRadio) { if (gridRadioOne.checked) { gridRadioChecked = gridRadioOne.value; break; } } if (isNaN(gridInput.value) || gridInput.value < 1 || gridInput.value > 100) { gridText.innerHTML = `Enter a <em>right</em> number <strong>from 1 to 100</strong>`; } else if (gridRadioChecked === "yellow") { gridText.innerHTML = ` The grid has <em>${gridInput.value}*${gridInput.value}</em> squares<br/> The mouse hover color is <strong>yellow</strong><br/> `; mouseHoveringColor = "yellow"; // value for doMouseHovering() } else if (gridRadioChecked === "randomize") { gridText.innerHTML = ` The grid has <em>${gridInput.value}*${gridInput.value}</em> squares<br/> The mouse hover color is <strong>randomize</strong><br/> `; mouseHoveringColor = "randomize"; // value for doMouseHovering() } else { gridText.innerHTML = ` The grid has <em>${gridInput.value}*${gridInput.value}</em> squares<br/> The mouse hover color is <strong>randomize every square</strong><br/> `; mouseHoveringColor = "randomizeEverySquare"; // value for doMouseHovering() } gridSize = gridInput.value * gridInput.value; createGrid(); }); } function createGrid() { gridContainer.innerHTML = ""; // grid clean for new submit for (let i=0; i < (gridSize); i++) { gridSquares = document.createElement("div"); gridSquares.classList.add("gridSquares"); gridContainer.appendChild(gridSquares); gridSquares.style.cssText = ` width: calc(100% / ${gridInput.value}); height: calc(100% / ${gridInput.value}); background-color: #eee; border: 1px solid #ccc; `; gridContainer.style.cssText = ` height: 97vh; width: auto; display: flex; flex-wrap: wrap; aspect-ratio: 1/1; `; mouseSquares.push(gridSquares); // create array for doMouseHovering() } doMouseHovering(); } function doMouseHovering() { // need mouseBackgroundColor for calculate "randomize" (NOT for "randomizeEverySquare") let mouseBackgroundColor = ""; if (mouseHoveringColor === "yellow") { mouseBackgroundColor = "yellow"; } else if (mouseHoveringColor === "randomize") { let r = Math.floor(Math.random() * 256); let g = Math.floor(Math.random() * 256); let b = Math.floor(Math.random() * 256); mouseBackgroundColor = `rgb(${r}, ${g}, ${b})`; } mouseSquares.forEach(gridSquares => { gridSquares.addEventListener('mouseover', () => { if (mouseHoveringColor === "yellow") { gridSquares.style.backgroundColor = mouseBackgroundColor; } else if (mouseHoveringColor === "randomize") { gridSquares.style.backgroundColor = mouseBackgroundColor; } else { let r = Math.floor(Math.random() * 256); let g = Math.floor(Math.random() * 256); let b = Math.floor(Math.random() * 256); gridSquares.style.backgroundColor = `rgb(${r}, ${g}, ${b})`; } }); }); }
问题1:容器高度超出屏幕的最优解决
问题根源是浏览器默认给body设置了8px左右的margin,导致100vh的容器加上margin后总高度超过视口。
解决步骤
- 重置
body的默认边距,在CSS中修改:
body { display: flex; flex-flow: row wrap; justify-content: flex-start; margin: 0; /* 新增:清除默认margin */ padding: 0; /* 新增:清除默认padding */ }
- 将容器的基础样式移到CSS中,避免JS重复设置:
#gridContainer { display: flex; flex-wrap: wrap; aspect-ratio: 1/1; /* 保持正方形 */ height: 100vh; width: auto; max-height: 100vh; /* 防止溢出 */ overflow: hidden; /* 隐藏超出内容 */ }
- 简化JS中
createGrid函数里的容器样式设置,仅保留动态需要的部分(如果有的话),移除原有的gridContainer.style.cssText整段代码。
问题2:移动端保持正方形容器
核心是用aspect-ratio: 1/1配合媒体查询,让容器在移动端以宽度为基准,桌面端以高度为基准,自动维持正方形比例。
解决步骤
- 在CSS中添加媒体查询,区分移动端和桌面端:
/* 移动端(屏幕宽度≤768px) */ @media (max-width: 768px) { #gridContainer { width: 100vw; height: auto; max-width: 100vw; } } /* 桌面端(屏幕宽度>768px) */ @media (min-width: 769px) { #gridContainer { height: 100vh; width: auto; max-height: 100vh; } }
- 给
body添加横向溢出隐藏,避免移动端出现横向滚动条:
body { /* 原有样式 */ overflow-x: hidden; /* 新增 */ }
- 移除JS中硬编码的容器宽高设置,让CSS媒体查询主导响应式变化,JS仅负责生成网格方块的尺寸。
额外优化建议
- 将网格方块的固定样式移到CSS类中,减少JS DOM操作:
.gridSquares { background-color: #eee; border: 1px solid #ccc; }
然后在JS的createGrid函数中,仅动态设置宽高:
gridSquares.style.width = `calc(100% / ${gridInput.value})`; gridSquares.style.height = `calc(100% / ${gridInput.value})`;
- 每次创建新网格时清空
mouseSquares数组,避免重复绑定事件:
function createGrid() { gridContainer.innerHTML = ""; mouseSquares = []; // 新增:清空数组 // 后续代码... }
内容的提问来源于stack exchange,提问作者OlKlym
相关产品推荐
相关产品推荐

