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
相关产品推荐
相关产品推荐

