移动端布局加载餐厅结果时mapPanel被挤出视口的问题
小屏垂直布局下mapPanel被挤出视口的修复方案
问题场景
开发餐厅搜索网站,核心布局包含:
mapPanel:负责位置选择与搜索参数配置tablePanel:展示餐厅搜索结果
适配小屏设备时,将布局调整为mapPanel在上、tablePanel在下的垂直结构,但搜索加载结果后,mapPanel会被向上推挤出视口。需求是mapPanel保持固定尺寸,tablePanel在其下方独立滚动。
原布局代码
CSS 代码
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap'); * { margin: 0; padding: 0; box-sizing: border-box; } html, body { display: flex; flex-direction: column; width: 100%; height: 100%; min-width: 600px; min-height: 400px; background-color: rgb(215, 215, 215); align-items: center; } header { display: flex; justify-content: center; align-items: center; padding: 10px 0; background-color: rgb(67, 106, 223); width: 100%; } h1, h2, h4, div, input, button, th, td { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial; font-weight: normal; } h1 { width: 35%; text-align: center; flex-shrink: 0; max-width: 638px; font-size: 1.4em; } h2 { text-align: center; flex-shrink: 0; width: 65%; max-width: 1548px; font-size: 1.2em; } h4 { font-size: 1em; } #main { display: flex; justify-content: center; width: 100%; height: 100%; min-width: 600px; min-height: 400px; } #content { flex-grow: 1; flex-shrink: 1; display: flex; justify-content: center; height: 100%; max-width: 2380px; } #mapPanel { display: flex; flex-direction: column; flex: 1 1 35%; max-width: 637px; min-width: 200px; min-height: 200px; height: 100%; background-color: #f8f8f8; padding: 20px; overflow: hidden; align-items: stretch; } #map { flex: 1 2 auto; align-self: center; width: 100%; min-height: 170px; max-height: 600px; max-width: 600px; background-color: #ddd; margin-bottom: 20px; border: none; } #optionsContainer { display: flex; flex-direction: column; flex: 0 0 auto; width: 100%; min-height: 149px; } #searchContainer { display: flex; min-width: 0; flex-flow: row wrap; margin-bottom: 10px; gap: 10px; } #currentLatitude, #currentLongitude { flex-grow: 1; padding: 10px; } #currentLatitude:focus, #currentLongitude:focus { outline: none; } #search { flex-grow: 1; padding: 10px; cursor: pointer; } #locateMe { align-self: center; padding: 5px 10px; margin-bottom: 20px; cursor: pointer; } #radiusContainer { margin-bottom: 20px; } #radiusContainer h4 { margin-bottom: 10px; } #radiusContainer div { display: flex; align-items: center; flex-flow: row wrap; } #radiusSlider { margin-right: 10px; cursor: pointer; } #minimumRatingContainer { display: flex; flex-direction: column; margin-bottom: 20px; } #minimumRatingContainer h4 { margin-bottom: 10px; } #minimumRatingContainer div { display: flex; gap: 10px; flex-flow: row wrap; } .rating { flex-shrink: 0; padding: 5px 10px; background-color: white; border: 1px solid #ccc; border-radius: 20px; cursor: pointer; transition: background-color 0.4s, box-shadow 0.3s; } .rating:focus, .rating:active { outline: none; background-color: yellow; box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2); border-radius: 20px; } .rating:active { transform: scale(0.99); } #orderByContainer { margin-bottom: 20px; } #orderByContainer h4 { margin-bottom: 10px; } #orderByContainer div { display: flex; align-items: center; flex-flow: row wrap; gap: 10px; } .order { padding: 5px 10px; background-color: white; border: 1px solid #ccc; border-radius: 20px; cursor: pointer; transition: background-color 0.4s, box-shadow 0.3s; } .order:focus, .order:active { outline: none; background-color: yellow; box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2); border-radius: 20px; } .order:active { transform: scale(0.99); } #tablePanel { flex: 1 2 65%; overflow-y: auto; padding: 20px; background-color: white; } table { table-layout: fixed; border-collapse: collapse; overflow: scroll; width: 100%; } th, td { border: 2px solid black; overflow: hidden; text-overflow: ellipsis; padding: 8px; text-align: center; white-space: nowrap; } th { background-color: #f2f2f2; } .longth { display: inline; } .shortth { display: none; } th:nth-child(1), td:nth-child(1) { width: 35%; } th:nth-child(2), td:nth-child(2) { width: 15%; } th:nth-child(3), td:nth-child(3) { width: 20%; } th:nth-child(4), td:nth-child(4) { width: 15%; } th:nth-child(5), td:nth-child(5) { width: 15%; } td img { max-width: 100px; height: auto; display: block; margin: 0 auto; } /* Use shorter versions of table headers if window is too small*/ @media (max-width: 1240px) { .longth { display: none; } .shortth { display: inline; } } /* For small devices */ @media (max-width: 1018px) { h2 { width: 0px; display: none; } #content { flex-direction: column; align-items: center; } #mapPanel { flex: 0 0 auto; width: 100%; max-width: none; height: 570px; min-height: 570px; padding: 20px 20px 0 20px; overflow: unset; } #map { flex: 0 0 auto; max-width: none; min-height: 226px; } #tablePanel { flex: 4 0 auto; width: 100%; overflow-y: unset; } table { overflow: unset; table-layout: fixed; border-collapse: collapse; } #options { flex-direction: column; } #bottomOptionsContainer { flex-direction: column; } #radiusSlider {} }
HTML 代码
<header> <h1>Restaurant Finder</h1> <h2>Results</h2> </header> <div id="main"> <div id="content"> <div id="mapPanel"> <div id="map"></div> <div id="optionsContainer"> <div id="searchContainer"> <input type="text" id="currentLatitude" placeholder="Latitude"></input> <input type="text" id="currentLongitude" placeholder="Longitude"></input> <button id="search">Search</button> </div> <button id="locateMe">Locate me</button> <div id="radiusContainer"> <h4>Radius:</h4> <div> <input type="range" orient="vertical" id="radiusSlider" min="200" max="900" value="500" step="100"> <span id="radiusValue">500</span> meters </div> </div> <div id="minimumRatingContainer"> <h4>Minimum rating:</h4> <div> <button class="rating">2.5</button> <button class="rating">3.0</button> <button class="rating" style="background-color: yellow">3.5</button> <button class="rating">4.0</button> <button class="rating">4.5</button> </div> </div> <div id="orderByContainer"> <h4>Order the results by:</h4> <div> <button class="order" style="background-color: yellow">Rating</button> <button class="order">Distance</button> <button class="order">Price level</button> <button class="order">Number of ratings</button> </div> </div> </div> </div> <div id="tablePanel"> <table id="results"> <thead> <tr> <th>Name</th> <th>Rating</th> <th><span class="longth">Number of ratings</span><span class="shortth">Ratings</span></th> <th><span class="longth">Price level</span><span class="shortth">Price</span></th> <th>Directions</th> </tr> </thead> <tbody> </tbody> </table> </div> </div> </div>
问题根源
- 小屏媒体查询中
#tablePanel设置了overflow-y: unset,导致搜索结果过多时,tablePanel会被内容撑开,进而把mapPanel推挤出视口 #content和#main在垂直布局下没有明确的高度约束,整体内容会超出视口高度
修复方案
修改小屏媒体查询(@media (max-width: 1018px))中的以下样式:
1. 约束#main和#content的高度
确保整体布局高度不超出视口:
#main { height: calc(100vh - 60px); /* 减去header的高度,根据实际调整 */ } #content { flex-direction: column; align-items: center; height: 100%; /* 继承父容器高度 */ }
2. 修复#mapPanel的溢出设置
恢复overflow: hidden,防止内部内容撑开面板:
#mapPanel { flex: 0 0 auto; width: 100%; max-width: none; height: 570px; min-height: 570px; padding: 20px 20px 0 20px; overflow: hidden; /* 替换原有unset */ }
3. 让#tablePanel独立滚动
恢复垂直滚动,并设置它占据mapPanel下方的剩余空间:
#tablePanel { flex: 1; /* 占据剩余可用空间 */ width: 100%; overflow-y: auto; /* 恢复垂直滚动 */ }
完整修改后的小屏媒体查询代码
/* For small devices */ @media (max-width: 1018px) { h2 { width: 0px; display: none; } #main { height: calc(100vh - 60px); } #content { flex-direction: column; align-items: center; height: 100%; } #mapPanel { flex: 0 0 auto; width: 100%; max-width: none; height: 570px; min-height: 570px; padding: 20px 20px 0 20px; overflow: hidden; } #map { flex: 0 0 auto; max-width: none; min-height: 226px; } #tablePanel { flex: 1; width: 100%; overflow-y: auto; } table { table-layout: fixed; border-collapse: collapse; } #options { flex-direction: column; } #bottomOptionsContainer { flex-direction: column; } }
效果说明
mapPanel保持固定高度,不会被内容挤压tablePanel占据mapPanel下方的剩余视口空间,内容过多时内部垂直滚动,不会影响整体布局- 整体页面不会出现滚动条,所有内容约束在视口范围内
内容的提问来源于stack exchange,提问作者Adibou
相关产品推荐
相关产品推荐

