Specifically on mobile, scroll a div from anywhere

2019-08-19 03:00发布

问题:

From the original question, we have successfully used D3js to detect a scroll event, which we use to scroll the div #scroll-content from anywhere; however, this solution works for desktop but is not working the same on mobile. Example of the issue:

  • gh-pages - example works on desktop but not on mobile.
  • jsfiddle - example works on desktop but not on mobile.
  • (or see attached code snippet)

On mobile, how can we scroll the div #scroll-content from anywhere on the page? A successful solution means that we can place a finger anywhere on the mobile page and slide vertically to scroll the div #scroll-content.

Note the following:

  1. answer should allow the use of CSS Grid.
  2. answer can include d3.v4
  3. answer should NOT use jQuery (as is suggested in answer0)
  4. answer should NOT use plugins (as is suggested in answer1 and in answer2).
  5. answer should work on iPhone SE using both Safari and Chrome.

var body = d3.select("body");
var scroll = d3.select("#scroll-content");

body.on("wheel", function() {
    scroll.property("scrollTop", +scroll.property("scrollTop") + d3.event.deltaY)
});
body, html {
  height: 100%;
  box-sizing: border-box;
  overflow:hidden; /* stops scroll from the entire page */
  background-color: #ad6364;
}
#wrapper {
  display: grid;
  height:100%;
  grid-template-columns: 25% repeat(10,7%) 5%;
  grid-template-rows: 5% repeat(4,15%) 30% 5%;
  grid-template-areas:
    "nav nav nav nav nav nav nav nav nav nav nav nav"
    "side1 side1 viz1 viz1 viz1 viz1 viz1 viz1 viz1 viz1 viz1 side2"
    "side1 side1 viz1 viz1 viz1 viz1 viz1 viz1 viz1 viz1 viz1 side2"
    "side1 side1 viz1 viz1 viz1 viz1 viz1 viz1 viz1 viz1 viz1 side2"
    "side1 side1 viz1 viz1 viz1 viz1 viz1 viz1 viz1 viz1 viz1 side2"
    "int   int  int  int  int  int  int  int  int  int  int  int"
    "ftr   ftr  ftr  ftr  ftr  ftr  ftr  ftr  ftr  ftr  ftr  ftr";
  grid-gap:1px;
}

#scroll-content {
  display:block;
  grid-column: 1/3;
  grid-row: 2 / 6;
  /*background-color:#ad6364;*/
  /*opacity: 0.75;*/
  overflow: auto;
  background-color:#2B3033;
}
.step{
  margin-bottom: 1000px;
  overflow: auto;
  max-height: 100vh;
  position: relative;
  fill:#bdbdc1;
}

#viz-container{
  background-color:#2B3033;
  grid-area: viz1;
}
#nav-bar{
  grid-area:nav;
  background-color:#767678;
}
#side-bar1{
  grid-area:side1;
  background-color:#767678;
}
#side-bar2{
  grid-area:side2;
  background-color:#767678;
}
#footer{
  grid-area:ftr;
  background-color:#767678;
}
#interaction{
  grid-area:int;
  background-color:#767678;
}
.general-text{
  text-align: center;
  vertical-align: middle;
  color:#bdbdc1;
  font-size:12px;
}
<html>
  <head>
    <meta charset="utf-8">
    
    <!-- D3 -->
    <script src="https://d3js.org/d3.v5.min.js"></script>

    <!-- linked CSS -->
    <link href="style.css" type="text/css" rel="stylesheet">
  </head>
  <body>
    <div id="wrapper">
      <div id="nav-bar" class="general-text">nav</div>
      <div id="scroll-content" class="general-text">
        <section class="step">Question: ON MOBILE, how do we make this div #scroll-content scrollable from anywhere on the page? <br><br>Notice ON DESKTOP that if we place the mouse anywhere, this div currently scrolls, which is the correct behavior desired for MOBILE; however, ON MOBILE this div can not be scrolled. <br><br>...scroll down.
        </section>
        <section class="step">Note: none of the div positions are fixed b/c we're using CSS Grid.<br><br>...scroll down.
         </section>
         <section class="step">...the end.
         </section>
      </div>
      <div id="viz-container" class="general-text">viz</div>
      <div id="interaction" class="general-text">interaction</div>
      <div id="side-bar1"></div>
      <div id="side-bar2"></div>
      <div id="footer" class="general-text">footer</div>
    </div>
    <!-- create listener -->
    <script src="scroll.js" type="text/javascript"></script>
  </body>
</html>

回答1:

The wheel event is fired when a mouse wheel is scrolled. Mobiles do not have a mouse, instead have a touch screen.

A more generic event, the scroll event will fire when a scroll is detected, the browser can decide when this is, meaning it works for both desktop mouse wheel scrolling, and touch device scrolling.

However, it appears that the scroll event won't fire if the page height is less than 100% of viewport height. I am unsure of whether this is true with the wheel event. If it is, then the below solution will need to also include mouse events (mousedown, mousemove etc.) in additional to touch events.


Considering you want more customized behaviour, in which you scroll the element regardless of where the user scrolls on the screen, and that the page doesn't need to physically scroll, you just want to capture that event and use it, it may be required to create a custom scrolling function; use the touchstart, touchmove, and touchend events on the document.body to fire the custom scroll code for your #scroll-content. You would also use the wheel event to fire the scroll code too, so it works for desktops too.

This would look something like this:

  1. on touch start, store the y coordinate of the touch. startY = e.touches[0].pageY;
  2. on touch move, find the delta between the start y and the move y. deltaY = startY-moveY;
  3. fire the custom scroll event with this delta. customScroll(deltaY)

The custom scroll event will take a delta and apply this to the #scroll-content using something like element.scrollBy(0, deltaY)

You would also likely need to normalize the deltaY as the wheel deltaY may be a different magnitude e.g. *100 or inverse etc.


This is just a simplified description of how you could achieve your desired result. If you don't want ot use a plugin/library such as hammer.js then you will need to write the custom code yourself. Good luck.