CSS scroll-snap-type with Horizontal Scrolling
Asked Answered
A

3

12

I made a website that makes use of Bootstrap 4, horizontal scrolling (via jquery.mousewheel.js) and native CSS Snapping. I am using Chrome 81, and don't care about old/unsupported browsers.

When I apply scroll-snap-type: x mandatory; to CSS, the horizontal scrolling stops working (no scrolling happens at all). If the CSS property is removed, the scrolling behaves normally. Here is my HTML structure:

<div class="portfolio_content container-fluid h-100">
  <div class="row h-100 flex-row flex-nowrap scrollx long_content">
    <div class="col-12 h-100 project d-table" id="project1">
      <div class="project_title d-table-cell align-middle">
        <p>
          Project 1
        </p>
      </div>
    </div>
    <div class="col-12 h-100 project d-table" id="project2">
      <div class="project_title d-table-cell align-middle">
        <p>
          Project 2
        </p>
      </div>
    </div>
    <div class="col-12 h-100 project d-table" id="project3">
      <div class="project_title d-table-cell align-middle">
        <p>
          Project 3
        </p>
      </div>
    </div>
    <div class="col-12 h-100 project d-table" id="project4">
      <div class="project_title d-table-cell align-middle">
        <p>
          Project 4
        </p>
      </div>
    </div>
  </div>
</div>

For CSS, the key entries are here:

html, body {
    height: 100%;
}
.portfolio_content .scrollx {
    overflow-x: scroll;
}

.portfolio_content .long_content {
    scroll-snap-type: x mandatory; /* Comment this to make scrolling works */
}
.portfolio_content .project {
    scroll-snap-align: start;
}
.portfolio_content .project_title {
    display: block;
    margin: auto;
    text-align: center;
}
.portfolio_content .project_title h1 {
    font-size: 96px;
    line-height: 0.8;
}

Last, here is the JSFiddle demo. If you comment line 9 in CSS, the demo will be able to scroll.

My aim is to allow the "project" DIV to be snapped when scrolling, i.e. one "project" DIV will be fully shown after scrolling.

How should I change the codes?

Afterimage answered 14/5, 2020 at 9:59 Comment(8)
Did you notice that the text-align: center rule is preventing the text in your .project_title from showing?Khadijahkhai
but this does not relate to scrolling.Afterimage
Maybe not directly, but I removed the scroll-snap-type property and it still didn't scroll at all so it looks like there are problems with your CSS that you'll want to solve before you worry about scroll snapping.Khadijahkhai
Even I commented out the text-align: center, the situation doesn't change at all. The CSS property is just for illustration purpose, irrelevant to the problem.Afterimage
Don't use any mousewheel js libraries, they are in 99..9% the cause of problems like yours.Alboin
@Alboin but how do I solve the problem?Afterimage
Do you really need mousewheel or you just want a scroll and snap behaviour?Hypozeuxis
And does your whole website would be horizontal scrolling or you also need vertical?Hypozeuxis
E
2

You dont need an external plugin when you can do it with a simple script.

$(".portfolio_content .project").on('wheel', function(e) {
    e.preventDefault();        
    if (e.originalEvent.wheelDelta >= 0) {
        if ($(this).prev().length) {
            var prev = "#" + $(this).prev().attr("id");
            document.querySelector(prev).scrollIntoView({behavior: 'smooth'});
        }
    } else {
        if ($(this).next().length) {
            var next = "#" + $(this).next().attr("id");
            document.querySelector(next).scrollIntoView({behavior: 'smooth'});
        }
    }
});
html, body {
    height: 100%;
}
.portfolio_content .scrollx {
    overflow-x: scroll;
}

.portfolio_content .project {
	scroll-snap-align: start;
}
.portfolio_content .project_title {
	display: block;
	margin: auto;
	text-align: center;
}
.portfolio_content .project_title h1 {
	font-size: 96px;
	line-height: 0.8;
}
#project1, #project3 {
    background-color: #EEE;
}
#project2, #project4 {
    background-color: #CCC;
}
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>

<div class="portfolio_content container-fluid h-100">
  <div class="row h-100 flex-row flex-nowrap scrollx long_content">
    <div class="col-12 h-100 project d-table" id="project1">
      <div class="project_title d-table-cell align-middle">
        <p>
          Project 1
        </p>
      </div>
    </div>
    <div class="col-12 h-100 project d-table" id="project2">
      <div class="project_title d-table-cell align-middle">
        <p>
          Project 2
        </p>
      </div>
    </div>
    <div class="col-12 h-100 project d-table" id="project3">
      <div class="project_title d-table-cell align-middle">
        <p>
          Project 3
        </p>
      </div>
    </div>
    <div class="col-12 h-100 project d-table" id="project4">
      <div class="project_title d-table-cell align-middle">
        <p>
          Project 4
        </p>
      </div>
    </div>
  </div>
</div>
Erle answered 3/6, 2020 at 15:32 Comment(4)
The script doesn't work. It does not convert vertical scrolling to horizontal scrolling.Afterimage
In my Fiddle, if you scroll vertically, the DIV content will scroll horizontally. However in your code snippet, there is no such function. I have to scroll horizontally by myself.Afterimage
What browser are you using? Because my snippet works fine in all Chromiums browsers for what asking for in your question. It looks completely different on Firefox, and thats not my fault, I copied the code you shared.Erle
I'm using the latest Chrome. You try to Run code snippet and click Full page. It's OUT OF CONTROL.Afterimage
T
1

You need to convert the mousewheel delta from X to Y and apply it to the element which has the scrollbar, also prevent default on the vertical mousewheel interaction of the container, but you need it to set it to the width of the container or it won't be enough to push.

 // 'swipe' left or right with mouse wheel:
const container = document.querySelector('.long_content');

container.addEventListener('wheel', (evt) => {

evt.preventDefault(); 

//scroll deirection
 let delta = evt.deltaY;
 //take widht of container:
let contWidth = evt.target.offsetWidth;

// check direction and apply to contWidth
if(delta < 0){
contWidth = -contWidth;
}
     
  container.scrollLeft += contWidth;  

});

Here is the updated fiddle

Totality answered 26/7, 2022 at 14:50 Comment(0)
P
0

Short answer: take away the javascript code.

Long answer: mousewheel.js is useful if you want to check or do something on wheel events. In this case, you simply want to let the browser scroll. You are taking control of that event in the function and preventing the default, but the default action is exactly what you want.

When you execute this.scrollLeft -= delta;, it does two things: scroll and then end scroll. the scroll-snap-type listens to end scroll and snaps the element. Actually, if you take away only the e.preventDefault(); line, a funny thing happens. The element vibrates and then changes. In my understanding, what is happening is: 1) Default scroll event, which takes some time. Meanwhile, 2) wheel event, which triggers a much shorter scroll event. 3) snap. Since the deltaX from 2 is small, you get to the starting position. repeat 2 and 3 several times. 4) Default scroll event ends with a much larger deltaX. 5) snap.

In summary, for the behavior you want, it is better to let the browser do the scrolling. If you want to do something else, you need to avoid sending an end scroll signal every time a mousewheel event fires. To do that, you can simply eliminate the two lines of code inside the mousewheel function and add the code you need.

EDIT after comment:

I should have guessed by the context, you want to transform vertical wheel events into horizontal wheel events. The former answer works if you send a horizontal wheel event (you can do that with a touchpad). The long answer still stands, so the question is how to bypass the problem. I thought of a couple of strategies:

  • Cancel the event and create a synthetic WheelEvent. By far the most elegant solution, but I could not make it work. The event gets dispatched but no scrolling occurs:
 let lc = document.querySelector('.long_content');
 lc.addEventListener('wheel', function(e) {
   if (e.deltaY !== 0){
     e.preventDefault();
     let evt = new WheelEvent('wheel', {
       deltaX: e.deltaY
     });
     this.dispatchEvent(evt);
   }
   else{
     // The new event gets captured here
   }
  });

EDIT 2

There are many strategies to make this work, but all of them have their disadvantages. The previous one had minimal impact on your script, but the change started after the wheel event ended. This and other parameters are left to the browser, and will not be under your control.

I have added a second fiddle with another strategy that gives the behavior you seem to want (https://jsfiddle.net/vhywmdb5/). I hope it helps, but every solution needs to be tweaked and there are no warranties that it will work with every browser. Unfortunately, the event simulation in mousewheel.js is broken at this time.

Peoples answered 22/5, 2020 at 11:55 Comment(3)
But I have to change the vertical scrolling to horizontal scrolling, thus I have to use the JS codes. Any alternative solution?Afterimage
I have updated the answer. Perhaps the fiddle is useful to you.Peoples
thanks for the update. The Fiddle looks laggy when scrolling, especially when I scroll up (making the content scrolling to the left)Afterimage

© 2022 - 2024 — McMap. All rights reserved.