“This is the first day of my participation in the First Challenge 2022. For details: First Challenge 2022”

Mobile terminal to prevent popover lower page sliding method

During H5 development, popovers are required in many scenarios

When a popover occurs, it is expected that the page below the popover cannot be slid in most scenarios

You certainly don’t expect popovers to slide

Recently the liver page encountered this problem again

Here are some of the most common approaches, along with some boundary cases and coping strategies

overflow:hidden

The most popular way is to set overflow: Hidden for elements

Setting the body prevents the page from sliding

document.body.style.overflow = 'hidden'
document.body.style.overflow = 'visible'
Copy the code

This works in most cases

But on some machines, this doesn’t work:

Popup window mask

There is also a case where some elements of the page have local sliding

<body style="overflow:hidden;">
  <div style="overflow:scroll; height:100%;">
    <! -- more element -->
  </div>
</body>
Copy the code

The current situation sets overflow:hidden for the body to still have no effect

Add the following mask to the popover, and under normal circumstances, the lower element will not receive the TouchMove event

<body style="overflow:hidden;">
  <div style="overflow:scroll; height:100%;">
    <! -- more element -->
  </div>

  <! -- dialog -->
  <div class="dialog">
    <! - mask - >
    <div class="mask" style="position:fixed; inset:0;"></div>
    <div class="content"></div>
  </div>
</body>
Copy the code

Where the inset property is short for left,top,right,bottom

On some models, however, the underlying element still receives the TouchMove event and slides accordingly

So you need to sacrifice the following method

prevent touchmove

Prevents the default behavior of the touchswipe event TouchMove

const touchHandle = function(e) {
  e.preventDefault()
}

// Popover events
{
  onShow(){
    document.body.addEventListener('touchmove', preventDefault, {
      passive: false}); },onHide(){
    document.body.removeEventListener('touchmove', preventDefault); }}Copy the code

Prevents the default behavior of the sliding event of the target element directly when the popover opens

This method is most efficient if the popover content is not slidable

If there is something to slide in the popover, and the sliding content is complicated

Then fine force control via TouchMove can be troublesome when preventing sliding events

position:fixed

There is also a common position:fixed

The target element is fixed when the popover opens and restored when it closes

Because positioning changes the position of the element on the page, you need to record the position of the element before fixing it

Unfixed scrolls the element back to its original position

// Popover events
{
  onShow(){
    document.body.style.top = `The ${document.body.getClientRects()[0].top
    }px`;
    document.body.style.position = 'fixed';
    document.body.style.left = '0';
    document.body.style.right = '0';
  },
  onHide(){
    document.body.style.position = 'visible';
    window.scrollTo(
      0.Math.abs(+document.body.style.top.replace('px'.' '))); }}Copy the code

Use class instead of style

This also happens to be found in the iOS low-end machine will be the above way after trying

There is still a problem, the phenomenon is as follows (TODO: complementary graph)

Lower pages will no longer slide, but masks and pop-ups as a whole can still be pulled down

A popover is a drop-down list popover whose position needs to be dynamically calculated, as shown in the following structure

<body style="overflow:hidden;">
  <! -- dialog -->
  <div class="dialog" style="top:88px;">
    <! - mask - >
    <div class="mask" style="position:fixed; inset:0;"></div>
    <! - content - >
    <div class="content"></div>
  </div>
</body>
Copy the code

The result is that the style and class Settings are displayed in the same way on this model

But the actual interactions are inconsistent

The fixed HTML structure looks like this, inserting a style tag in the element, using the class selector and! Important overrides the distance style

<body style="overflow:hidden;">
  <! -- dialog -->
  <div class="dialog" style="top:88px;">
    <! - mask - >
    <div class="mask" style="position:fixed; inset:0;"></div>
    <! - content - >
    <div class="content"></div>
    <style>
      .dialog{
        top:88px ! important;
      }
    </style>
  </div>
</body>
Copy the code

The following code

{
  onShow(){
    setTimeout(() = > {
      const dialogEl = document.querySelector<HTMLElement>('.dialog')
      if(! dialogEl) {return
      }
      const style = document.createElement('style')
      style.textContent = `
      .dialog{
        top:${dialogEl.style.top}! important; } `
      dialogEl.append(style)
    }, 200)}}Copy the code

It’s a very confusing operation, but it solves the problem

summary

For the abnormal scene that the lower page of the popup window on the mobile terminal can be slid

This article introduces four common solutions and a “puzzle operation.”

The demo presentation

  • The demo address

  • PC code scanning experience

  • Demo source code address