You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

移动端布局加载餐厅结果时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>&nbsp;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>

问题根源

  1. 小屏媒体查询中#tablePanel设置了overflow-y: unset,导致搜索结果过多时,tablePanel会被内容撑开,进而把mapPanel推挤出视口
  2. #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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 20:20:52