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

Bootstrap模态框中overflow-y: scroll的滚动异常问题

问题解决:Bootstrap模态框滚动列表选中选项时页面异常滚动

问题背景

使用Bootstrap创建的模态框中,包含两个通过Ajax填充的滚动列表(设置了overflow-y: scroll和固定高度)。选中列表下方的选项时,整个页面会滚动到该选项未设置滚动时的原始位置;尝试用overflow: revert修复,但小屏幕下列表无法滚动查看全部内容。

原因分析

这是浏览器的自动聚焦滚动行为导致的:当checkbox状态改变时,浏览器会自动将焦点聚焦到该元素,同时计算元素的位置并滚动页面。但元素被移动到另一个列表后,浏览器仍会基于它在原列表中的位置进行滚动,从而引发异常。

解决办法

在移动元素前后,手动保存并恢复列表的滚动位置,或者阻止浏览器的自动聚焦滚动。以下是两种可行方案:

方案1:保存并恢复滚动位置

修改selectElement函数,在移动元素前记录两个列表的滚动条位置,移动完成后恢复:

function selectElement(e) {
  // 保存当前两个列表的滚动位置
  const listaScrollTop = $("#lista").scrollTop();
  const listaSeleccionadosScrollTop = $("#listaSeleccionados").scrollTop();

  if ($(e).attr("seleccionado") == 0) {
    $(e).attr("seleccionado", "1")
      .parent().appendTo("#listaSeleccionados")
      .children("label").removeClass("btn-outline-success").addClass("btn-success");
  } else {
    $(e).attr("seleccionado", "0")
      .parent().prependTo("#lista")
      .children("label").removeClass("btn-success").addClass("btn-outline-success");
  }

  // 恢复滚动位置
  $("#lista").scrollTop(listaScrollTop);
  $("#listaSeleccionados").scrollTop(listaSeleccionadosScrollTop);
}

方案2:阻止元素聚焦引发的滚动

另一种方式是取消元素的焦点,阻止浏览器自动滚动行为:

function selectElement(e) {
  if ($(e).attr("seleccionado") == 0) {
    $(e).attr("seleccionado", "1")
      .parent().appendTo("#listaSeleccionados")
      .children("label").removeClass("btn-outline-success").addClass("btn-success");
  } else {
    $(e).attr("seleccionado", "0")
      .parent().prependTo("#lista")
      .children("label").removeClass("btn-success").addClass("btn-outline-success");
  }

  // 取消元素焦点,阻止浏览器自动滚动
  e.blur();
}

修改后的完整代码

JavaScript

function selectElement(e) {
  // 保存滚动位置
  const listaScrollTop = $("#lista").scrollTop();
  const listaSeleccionadosScrollTop = $("#listaSeleccionados").scrollTop();

  ($(e).attr("seleccionado") == 0) ? 
    $(e).attr("seleccionado", "1").parent().appendTo("#listaSeleccionados").children("label").removeClass("btn-outline-success").addClass("btn-success"): 
    $(e).attr("seleccionado", "0").parent().prependTo("#lista").children("label").removeClass("btn-success").addClass("btn-outline-success");

  // 恢复滚动位置
  $("#lista").scrollTop(listaScrollTop);
  $("#listaSeleccionados").scrollTop(listaSeleccionadosScrollTop);
}

$(document).ready(function() {
  $("#modalTest").on("show.bs.modal", function() {
    setTimeout(() => {
      $("#lista").html(`<li id="seleccionarTodos" class="mt-1"><button type="button" class="btn btn-outline-primary rounded-pill w-100" id="btnSeleccionarTodos" autocomplete="off">Seleccionar todos</button></li>`);
      for (i = 1; i <= 1000; i++) {
        $("#lista").append(`<li id="elemento` + i + `" class="mt-1">
                                        <input type="checkbox" id="chkElemento` + i + `" seleccionado="0" onchange="selectElement(this)" class="btn-check"  autocomplete="off">
                                        <label class="btn btn-outline-success w-100 check-lista" for="chkElemento` + i + `">
                                            <div class="text-start">Elemento ` + i + `</div>
                                        </label>
                                    </li>`);
      }
    }, 100);
  });
});

CSS

*::-webkit-scrollbar {
  max-width: 10px;
  max-height: 10px;
}

*::-webkit-scrollbar-track {
  border-radius: 0px 15px 15px 0px;
  box-shadow: inset 0 0 6px rgba(77, 0, 0, 0.192);
}

*::-webkit-scrollbar-thumb {
  max-width: 10px;
  border-radius: 0px 15px 15px 0px;
  background-color: rgba(204, 78, 78, 0.808);
  outline: 1px solid rgb(133, 84, 84);
}

.modal-content {
  border-radius: 20px;
}

.check-lista {
  border-radius: 15px;
  transition: all 0.2s ease;
  z-index: 1;
}

.check-lista:hover {
  transform: scale(1.02, 1.03);
  z-index: 10;
}

#lista,
#listaSeleccionados {
  min-height: 300px;
  max-height: 300px;
  overflow-y: scroll;
}

HTML

<!DOCTYPE html>
<html lang="es">

<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>Test</title>

  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>
  <script src="https://code.jquery.com/jquery-3.7.0.min.js" integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous"></script>

</head>

<body>
  <button type="button" class="position-absolute top-50 end-50 btn btn-primary btn-lg rounded-pill" data-bs-toggle="modal" data-bs-target="#modalTest">
            Abrir Modal
        </button>

  <div class="modal fade" id="modalTest" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="Modal Test" aria-hidden="true">
    <div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-xl">
      <div class="modal-content">
        <div class="modal-header">
          <h1 class="modal-title fs-5" id="titulo">Modal de prueba</h1>
          <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
        </div>
        <div class="modal-body">
          <div class="row row-cols-sm-1 row-cols-md-3">
            <div id="buscarEmpleado" class="col">
              <div class="row row-cols-1 gy-1">
                <div class="col">
                  <div class="input-group">
                    <input type="text" id="buscador" class="form-control" placeholder="Buscar" aria-label="Buscador" aria-describedby="Buscar">
                    <span class="input-group-text"><i class="bi bi-search"></i></span>
                  </div>
                </div>
              </div>
            </div>

            <div class="col px-2">
              <ul id="lista" class="list-unstyled p-1 border rounded-4 text-center">
                <li id="seleccionarTodos" class="mt-1"><button type="button" class="btn btn-outline-primary rounded-pill w-100" id="btnSeleccionarTodos" autocomplete="off">Seleccionar todos</button></li>
              </ul>
            </div>
            <div class="col px-2">
              <ul id="listaSeleccionados" class="list-unstyled p-1 border rounded-4 text-center oculto">
                <li id="deselTodos" class="mt-1"><button type="button" class="btn btn-primary rounded-pill w-100" id="btnDeseleccionarTodos" autocomplete="off">Deseleccionar todos</button></li>
              </ul>
            </div>
          </div>
          <hr>
          <div style="min-height:300px">
            <h4>Otras listas</h4>
          </div>
          <hr>
          <div style="min-height:100px">
            <h4>Filtros</h4>
          </div>
        </div>
        <div class="modal-footer text-center d-block">
          <button type="button" class="btn btn-danger rounded-pill" data-bs-dismiss="modal">Cancelar</button>
          <button type="button" class="btn btn-success rounded-pill">Guardar</button>
        </div>
      </div>
    </div>
  </div>
</body>

</html>

内容的提问来源于stack exchange,提问作者Sergio Domínguez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 17:07:29