Precondition
I am trying to make a number list picker that user can scroll on it.
The selected number was supposed to snap to the center
of the container.
Once a number was selected, it should be enlarged and changed the color.
Problems
- How can I snap the first or latest items to the center of the container?
- Is there a CSS or JavaScript way to detect a number is
snapping to the center then assign
selected
class on it? - Do not rely on any plugins.
* {
box-sizing: border-box;
font-family: Roboto, sans-serif;
}
.container {
display: flex;
flex-direction: row;
width: 10rem;
height: 22rem;
border-radius: 3rem;
border: solid 0.2rem #b2b2c2;
background-color: #000000;
display: flex;
align-items: center;
justify-content: center;
}
.scrollport {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
width: 9.4rem;
height: 22rem;
overflow: auto;
scroll-snap-type: y mandatory;
}
.cell {
display: block;
scroll-snap-align: center;
flex-grow: 1;
flex-shrink: 0;
flex-basis: 33.3%;
display: flex;
justify-content: center;
align-items: center;
color: #e9e9f2;
font-size: 2.4rem;
}
.selected {
font-size: 3rem;
font-weight: bold;
color: #0073e6;
}
<div class="container">
<div class="scrollport">
<div class="cell">09</div>
<div class="cell selected">10</div>
<div class="cell">11</div>
<div class="cell">12</div>
<div class="cell">13</div>
<div class="cell">14</div>
<div class="cell">15</div>
<div class="cell">16</div>
</div>
</div>
Precondition
I am trying to make a number list picker that user can scroll on it.
The selected number was supposed to snap to the center
of the container.
Once a number was selected, it should be enlarged and changed the color.
Problems
- How can I snap the first or latest items to the center of the container?
- Is there a CSS or JavaScript way to detect a number is
snapping to the center then assign
selected
class on it? - Do not rely on any plugins.
* {
box-sizing: border-box;
font-family: Roboto, sans-serif;
}
.container {
display: flex;
flex-direction: row;
width: 10rem;
height: 22rem;
border-radius: 3rem;
border: solid 0.2rem #b2b2c2;
background-color: #000000;
display: flex;
align-items: center;
justify-content: center;
}
.scrollport {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
width: 9.4rem;
height: 22rem;
overflow: auto;
scroll-snap-type: y mandatory;
}
.cell {
display: block;
scroll-snap-align: center;
flex-grow: 1;
flex-shrink: 0;
flex-basis: 33.3%;
display: flex;
justify-content: center;
align-items: center;
color: #e9e9f2;
font-size: 2.4rem;
}
.selected {
font-size: 3rem;
font-weight: bold;
color: #0073e6;
}
<div class="container">
<div class="scrollport">
<div class="cell">09</div>
<div class="cell selected">10</div>
<div class="cell">11</div>
<div class="cell">12</div>
<div class="cell">13</div>
<div class="cell">14</div>
<div class="cell">15</div>
<div class="cell">16</div>
</div>
</div>
Expect Result
Share Improve this question edited Sep 6, 2021 at 14:49 Penny Liu asked May 29, 2019 at 8:13 Penny LiuPenny Liu 17.6k5 gold badges86 silver badges108 bronze badges 4- 2 Something is not clear to me: do you want your number to be selected only when scrolling, without having to click on it? If so, how would you be able to select the first and last item in the list? You should also change your snippet IMO, because in current state, it shows exactly what you expect, so that people can be confused what to fix – Kaddath Commented May 29, 2019 at 8:17
- 1 Thanks for your ments. Give me a few minutes. I'll update my post very soon. – Penny Liu Commented May 29, 2019 at 8:26
- These are two questions. Let's stick to one. – yunzen Commented May 29, 2019 at 9:03
- 1 I made an edit to my answer to answer both your questions – yunzen Commented May 29, 2019 at 10:17
2 Answers
Reset to default 5Add pseudo elements to .scrollport
and style them like .cell
.scrollport:before,
.scrollport:after {
content: '';
}
.scrollport:before,
.scrollport:after,
.cell {
/* ... */
}
Also I created a JS function that utilizes David Walsh's debounce function to simulate a scrollend event. I then check for every cell
if it's center is around the scrollport
's center and set the classes accordingly
"use strict";
console.clear()
{
const selector = 'scrollport'
const selected = 'selected'
const scrollports = document.getElementsByClassName(selector)
for (const scrollport of scrollports) {
scrollport.addEventListener('scroll', debounce(check, 250) /* simulate scrollend event */ )
}
function check(e) {
// uses native elementFromPoint for better performance
const rect = e.target.getBoundingClientRect();
const centerCell = document.elementFromPoint(rect.left + e.target.offsetWidth / 2, rect.top + e.target.offsetHeight / 2)
for (const cell of e.target.getElementsByClassName(selected)) {
cell.classList.remove(selected)
}
centerCell.classList.add(selected)
// Old version for backward patibility
// const rect = e.target.getBoundingClientRect();
// for (const cell of e.target.children) {
// const cellRect = cell.getBoundingClientRect();
// const bounds = [rect.height/2 - cellRect.height/2, rect.height/2 + cellRect.height/2];
// const centerScrollOffset = cellRect.top - rect.top + cell.offsetHeight / 2
// if (bounds[0] < centerScrollOffset && centerScrollOffset < bounds[1]) {
// cell.classList.add(selected)
// } else {
// cell.classList.remove(selected)
// }
// }
}
}
// From: https://davidwalsh.name/javascript-debounce-function
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this,
args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
* {
box-sizing: border-box;
font-family: Roboto, sans-serif;
}
.container {
display: flex;
flex-direction: row;
width: 10rem;
height: 22rem;
border-radius: 3rem;
border: solid 0.2rem #b2b2c2;
background-color: #000000;
display: flex;
align-items: center;
justify-content: center;
}
.scrollport:before,
.scrollport:after {
content: '';
}
.scrollport {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
width: 9.4rem;
height: 22rem;
overflow: auto;
scroll-snap-type: y mandatory;
}
.scrollport:before,
.scrollport:after,
.cell {
display: block;
scroll-snap-align: center;
flex-grow: 1;
flex-shrink: 0;
flex-basis: 33.3%;
display: flex;
justify-content: center;
align-items: center;
color: #e9e9f2;
font-size: 2.4rem;
}
.selected {
font-size: 3rem;
font-weight: bold;
color: #0073e6;
}
<div class="container">
<div class="scrollport">
<div class="cell">09</div>
<div class="cell selected">10</div>
<div class="cell">11</div>
<div class="cell">12</div>
<div class="cell">13</div>
<div class="cell">14</div>
<div class="cell">15</div>
<div class="cell">16</div>
</div>
</div>
You can try this code here:
in this answer used jquery malihu-custom-scrollbar-plugin link and update some code here
(function($){
$(window).on("load",function(){
$(".scrollport").mCustomScrollbar({autoHideScrollbar:true});
});
})(jQuery);
* {
box-sizing: border-box;
font-family: Roboto, sans-serif;
}
.container {
display: flex;
flex-direction: row;
width: 10rem;
height: 22rem;
border-radius: 3rem;
border: solid 0.2rem #b2b2c2;
background-color: #000000;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.scrollport {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
width: 9.4rem;
height: 22rem;
overflow: auto;
}
.cell {
flex-basis: 120px;
height: 120px;
color: #e9e9f2;
font-size: 2.4rem;
display: flex;
align-items: center;
justify-content: center;
padding-left: 20px;
}
.selected {
font-size: 3rem;
font-weight: bold;
color: #0073e6;
}
<script src="https://cdnjs.cloudflare./ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/malihu-custom-scrollbar-plugin/3.1.5/jquery.mCustomScrollbar.concat.min.js"></script>
<link href="https://cdnjs.cloudflare./ajax/libs/malihu-custom-scrollbar-plugin/3.1.5/jquery.mCustomScrollbar.css" rel="stylesheet" />
<div class="container">
<div class="scrollport">
<div class="cell">09</div>
<div class="cell selected">10</div>
<div class="cell">11</div>
<div class="cell">12</div>
<div class="cell">13</div>
<div class="cell">14</div>
<div class="cell">15</div>
<div class="cell">16</div>
</div>
</div>