Modal pop-ups are very common in mobile development. As you are reading this article, you may have written a unified Modal popup component based on Vue, React, or applets. It seems that a small modal box contains a lot of knowledge. In this article, you will explore the problem of scroll chaining in modal boxes.

background

As the saying goes, the product has three treasures: popover, floating layer and guidance, enough to see the status of popover in the eyes of students. For any front-end students who just started, the realization of a modal box can basically reach the point of easy, but when the content of the modal box inside the scroll up, there will be a variety of confusing problems, among which, the most famous must be the scroll through.

So, what is roll penetration? That is, if the fixed pop-up window is displayed on the mobile end, sliding on the pop-up window will cause the lower page to scroll, which is called “scrolling through”. First, scroll penetration occurs on mobile terminals, not PCS. Now that you know what scroll penetration is and where it occurs, it’s time to get straight to business.

H5 rolling penetration

First of all, we need to understand that scrolling penetration occurs only when the content in the modal box is taller than the element itself, i.e., when scrolling.

Solution 1

When we scroll inside the modal box, the bottom content will also scroll, so if we disable the scroll event of the mask layer, the bottom content will not scroll naturally. (This article assumes that the modal box and mask layer are at the same level), no more nonsense, cui Hua, on the code.

<div class="popup" v-if="showPopDialog" @click="closePopDialog" @touchmove="touchForbidden"></div>

touchForbidden(e){
    e.preventDefault()
},
Copy the code

According to the above such after operation, the content of the modal inside the box is normal rolling, background will not touch with rolling, so, if we are already successfully solved the problem, but the actual situation is not so optimistic, after repeated tests, found that when at the top or bottom edge of the modal dialog to slide, still can trigger the content at the bottom of the slide.

Now that you’ve found the regularity of the trigger, then follow the rules go to solve the problem, ultimately, when open the modal box, we can carry out edge detection, when the user slide my hand is out of control to the modal box at the top or modal box at the bottom, we forbade sliding, so should can solve the above problem, cui flower, go to the code.

<div class="content" v-if="showPopDialog" @touchmove="touchMove" id="canmove" @touchstart="touchStart"> </div> </div> </div> </div>Copy the code

We first register the TouchStart event on the modal box to get the y coordinate value of the user’s first touch

touchStart(e){
     this.firstY = e.targetTouches[0].clientY;
}
Copy the code

Then, when the user slides on the modal frame, the y coordinate value in the sliding process is obtained. When the clientY > stratY of the contact point in the sliding process indicates that the sliding direction is downward; when the sliding distance is 0, it indicates that the sliding position of the user is the top of the modal frame. You now have the edge condition for the user to slide to the top of the modal box.

Similarly, clientY < startY indicates that the slide direction is up, and scrollTop + offsetHeight >= scrollHeight indicates that the slide has reached the bottom of the modal box.

touchMove(e){
    let target = document.getElementById('canmove')
    let offsetHeight = target.offsetHeight,
      scrollHeight = target.scrollHeight;
    let changedTouches = e.changedTouches;
    let scrollTop = target.scrollTop;
    if (changedTouches.length > 0) {
      let touch = changedTouches[0] || {};
      let moveY = touch.clientY;
      if(moveY > this.firsty && scrollTop === = 0) {// Slide to the top of popover critical condition e.preventDefault()return false
      } else if(moveY < this.firsty && scrollTop + offsetHeight >= scrollHeight) {// Slide bottom critical condition e.preventDefault()return false}}}Copy the code

Ok, next test, no problem found, perfect solution, solution 1 Done.

In the process of solving the sliding problem, there will always be clientHeight, offsetHeight, scrollHeight, scrollTop and so on. It is necessary to review a wave of basic knowledge about them.

  • OffsetHeight has nothing to do with the scrolling of an element. It only represents the height of the element, including border, padding, horizontal scroll bar but not margin
  • ClientHeight is similar to offsetHeight except that it only includes padding and excludes border, horizontal scroll bar, and margin.

  • ScrollHeight is only meaningful if the element is scrolling. When the element is not scrolling, scrollHeight==clientHeight. When the element is scrolling, scrollHeight is scrollTop + clientHeight

  • ScrollTop is the part of the element that is hidden when it scrolls
  • OffsetTop still has nothing to do with scrolling and represents the distance from the top of the current element to the top of the nearest parent element

Solution 2

Next, we will study scheme 2. When opening the popover, we can add dynamic class to the bottom content area to prevent the bottom content from sliding, but this will cause the problem of losing the scrolling distance of the bottom content area. Nothing, no panic! We can record the scrollTop before we lose it. When we close the popover, we can set the scrollTop back. Theoretically feasible, then we will officially start coding…

When opening the modal box, we need to add a dynamic class to the bottom content area to prevent sliding, as follows:

.forbidden_scroll{
    position: fixed;
    height: 100%;
  }
Copy the code

Before opening the modal box, we need to record the scrollTop value of the current bottom content,

touchmove(e){
    this.scrollTop = document.getElementById('scrollElement').scrollTop
}
Copy the code

When closing the popover, remove the dynamic class you just added and set the scrollTop back.

closePopDialog(){
    this.showPopDialog = false
    this.top = -this.scrollTop
    this.showStyle = false
},
Copy the code

Part of the template code is as follows:

<div class="main">
    <div :class="showStyle ? 'forbidden_open' : 'article'" id="scrollElement" :style="{'margin-top': top + 'px'}" @touchmove="touchmove">
      <div class="block_red">
        <div class="block_click" @click="openPopDialog">Click Me</div>
      </div>
      <div class="block_yellow"></div>
      <div class="block_green"></div>
    </div>
    <div class="popup" v-if="showPopDialog" @click="closePopDialog" @touchmove="touchForbidden">
    </div>
    <div class="content" v-if="showPopDialog"> I'm here to test, I'm here to test, I'm here to test, I'm here to test, I'm here to test I'm here to test, I'm here to test, I'm here to test, I'm here to test, I'm here to test I'm here to test, I'm here to test, I'm here to test, I'm here to test, I'm here to test I'm here to test, I'm here to test, I'm here to test, I'm here to test, I'm here to test I'm here to test I'm here to test I'm here to test I'm here to test I'm here to test I'm here to test </div> </div>Copy the code

After testing, it was found that this scheme could also solve the problem of scrolling penetration. However, I tried sliding to the edge of the popover again, but it still triggered the sliding of the bottom content. There was no way, and edge detection like scheme 1 was still needed to solve the problem perfectly.

Both solutions discussed above are based on H5, and there is still no perfect solution for scrollthrough in applets. The fundamental reason is that applets don’t have the DOM concept and can’t manipulate the DOM as freely as in H5.

Other solution defects
  • @touchmove.prevent does not solve sliding to the edge and triggering sliding to the bottom
  • The overscroll behavior still cannot solve the above problems, and the compatibility is mysterious caniuse.com/#search=ove…

The above discussion is based on H5. In small programs, scrollview can be used in combination with the above solutions to solve such problems, but there is still the problem of sliding to the edge to trigger the bottom scroll, and there is no perfect solution at present.