Обзор сценария
Всплывающие окна — распространенный способ взаимодействия, а маска — обязательный элемент всплывающего окна, который используется для отделения страницы от блока всплывающего окна и временной блокировки взаимодействия страницы. Однако при скольжении по замаскированному элементу, когда вы скользите до конца содержимого и продолжаете скользить, страница в нижней части маски начнет прокручиваться.Очевидно, что это не тот эффект, который нам нужен, поэтому нам нужно предотвратить такое поведение.
Итак, как это остановить? Пожалуйста, посмотрите следующий анализ:
анализ случая
Вариант первый
- При включенной маске добавьте стиль к телу:
overflow: hidden;
height: 100%;
В некоторых моделях вам также может понадобиться добавить стили в корневой узел:
overflow: hidden;
- Когда маска отключена, вышеуказанные стили удаляются.
преимущество:Просто и удобно, просто добавьте стиль css, никакой сложной логики.
недостаток:Совместимость плохая, для ПК подходит, а мобильный терминал смущает. В некоторых моделях Android и сафари запретить прокрутку нижней страницы невозможно.
Если вам нужно применить его к мобильному терминалу, то вам может понадобиться второй вариант.
Вариант 2
Он заключается в использовании сенсорного события мобильного терминала для предотвращения поведения по умолчанию (здесь можно понять, что прокрутка страницы является поведением по умолчанию).
// node为蒙层容器dom节点
node.addEventListener('touchstart', e => {
e.preventDefault()
}, false)
Просто и грубо, нижняя страница не может двигаться при прокрутке. Если ваш замаскированный контент не будет иметь полос прокрутки, то описанный выше метод идеален.
Однако больше всего меня пугает внезапная тишина эфира: если в маскируемом контенте есть полоса прокрутки, она уже не сможет двигаться. Поэтому нам нужно написать некоторую логику js, чтобы определить, следует ли блокировать поведение по умолчанию, и сложность значительно возрастает.
Конкретная идея: определить, прокручивается ли замаскированный контент до конца, и если да, заблокировать поведение по умолчанию, в противном случае пусть оно будет безудержным.
Пример кода выглядит следующим образом:
<body>
<div class="page">
<!-- 这里多添加一些,直至出现滚动条 -->
<p>页面</p>
<p>页面</p>
<button class="btn">打开蒙层</button>
<p>页面</p>
</div>
<div class="container">
<div class="layer"></div>
<div class="content">
<!-- 这里多添加一些,直至出现滚动条 -->
<p>蒙层</p>
<p>蒙层</p>
<p>蒙层</p>
</div>
</div>
</body>
body {
margin: 0;
padding: 20px;
}
.btn {
border: none;
outline: none;
font-size: inherit;
border-radius: 4px;
padding: 1em;
width: 100%;
margin: 1em 0;
color: #fff;
background-color: #ff5777;
}
.container {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 1001;
display: none;
}
.layer {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 1;
background-color: rgba(0, 0, 0, .3);
}
.content {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 50%;
z-index: 2;
background-color: #f6f6f6;
overflow-y: auto;
}
const btnNode = document.querySelector('.btn')
const containerNode = document.querySelector('.container')
const layerNode = document.querySelector('.layer')
const contentNode = document.querySelector('.content')
let startY = 0 // 记录开始滑动的坐标,用于判断滑动方向
let status = 0 // 0:未开始,1:已开始,2:滑动中
// 打开蒙层
btnNode.addEventListener('click', () => {
containerNode.style.display = 'block'
}, false)
// 蒙层部分始终阻止默认行为
layerNode.addEventListener('touchstart', e => {
e.preventDefault()
}, false)
// 核心部分
contentNode.addEventListener('touchstart', e => {
status = 1
startY = e.targetTouches[0].pageY
}, false)
contentNode.addEventListener('touchmove', e => {
// 判定一次就够了
if (status !== 1) return
status = 2
let t = e.target || e.srcElement
let py = e.targetTouches[0].pageY
let ch = t.clientHeight // 内容可视高度
let sh = t.scrollHeight // 内容滚动高度
let st = t.scrollTop // 当前滚动高度
// 已经到头部尽头了还要向上滑动,阻止它
if (st === 0 && startY < py) {
e.preventDefault()
}
// 已经到低部尽头了还要向下滑动,阻止它
if ((st === sh - ch) && startY > py) {
e.preventDefault()
}
}, false)
contentNode.addEventListener('touchend', e => {
status = 0
}, false)
Хотя проблема решена, оглядываясь назад, можно сказать, что сложность и объем кода явно увеличились по градиенту. Исходя из принципа простоты и удобства, можем ли мы изучить другие решения?
Поскольку определение события касания более сложное, почему бы не выйти за рамки этого правила, найти другой способ и изучить более подходящее решение. Итак, у нас есть третий план.
третье решение
Если говорить о моем размышлении, так как мы хотим предотвратить прокрутку страницы, почему бы не исправить это в области просмотра (т.position: fixed
), чтобы его нельзя было прокрутить, и отпустите, когда маска будет закрыта.
Конечно, есть еще некоторые детали, которые нужно учитывать.После того, как страница будет прикреплена к окну, содержимое вернется наверх.Здесь нам нужно записать и синхронизировать верхнее значение.
Образец кода:
let bodyEl = document.body
let top = 0
function stopBodyScroll (isFixed) {
if (isFixed) {
top = window.scrollY
bodyEl.style.position = 'fixed'
bodyEl.style.top = -top + 'px'
} else {
bodyEl.style.position = ''
bodyEl.style.top = ''
window.scrollTo(0, top) // 回到原先的top
}
}
Резюме мыслей
- Если сценарий приложения — ПК, мы рекомендуем решение 1, которое действительно не слишком удобно.
- Если сценарий приложения — h5, вы можете использовать решение 2, но я предлагаю вам использовать решение 3.
- Если сценарий приложения — вся платформа, то нельзя упускать третий вариант
На этом статья подходит к концу, здесь я настоятельно рекомендую третье решение, потому что оно простое, удобное и имеет хорошую совместимость, после упаковки его можно использовать постоянно.