I tried to build a website similar to this. To do this I wrote the following code:
var $window = $(window);
var lastScrollTop = 0;
var shown_elements = 0;
var next_element = 0;
$window.on('scroll', scroll_to_next);
$window.trigger('scroll');
var window_height = $window.height();
var window_top_position = $window.scrollTop();
var window_bottom_position = (window_top_position + window_height);
var scroll = $(window).scrollTop();
function scroll_to_next() {
shown_elements = Math.ceil($(window).scrollTop() / window_height);
next_element = shown_elements * window_height;
if (lastScrollTop < $(window).scrollTop() && lastScrollTop != 0) {
//$window.scrollTop(next_element);
$('html, body').animate({
scrollTop: next_element
}, 'slow');
//alert(next_element);
}
lastScrollTop = $(window).scrollTop();
}
This is the HTML:
<div class="showroom1">
<div class="showroom_left">
<img class="fixed" src="mokups/frame.png">
<img class="screen" src="mokups/screen2.png">
</div>
<div class="showroom_right">
<h3>Hello</h3>
<h4>description</h4>
</div>
</div>
<div class="showroom2">
<div class="showroom_left">
<img class="screen" src="mokups/screen1.png">
</div>
<div class="showroom_right">
<h3>Hello</h3>
<h4>description</h4>
</div>
</div>
<div class="showroom1">
<div class="showroom_left">
<img class="fixed" src="mokups/frame.png">
<img class="screen" src="mokups/screen2.png">
</div>
<div class="showroom_right">
<h3>Hello</h3>
<h4>description</h4>
</div>
</div>
This code runs the first time. And scroll automatically to the second "showroom". But when I try to scroll a second time the page will not scroll.
What is wrong with my code?
I created a JSFiddle a while a go for another question with the same issue. https://jsfiddle.net/8zgsdzy0/
Here is the code:
var lastScrollPos = 0;
var scrollFired = false;
var textConainersElement = jQuery('.text-container p');
var mainElem = jQuery("[data-main='true']");
var firstElem = jQuery("section:first-child");
var lastElem = jQuery("section:last-child");
var wrapper = jQuery(".wrapper");
jQuery(document).on('DOMMouseScroll mousewheel', function(e) {
// if the scroll has occrued already then dont fire it again until transition ended
if (scrollFired == true) {
jQuery(window).scrollTop(lastScrollPos);
return false;
}
var inviewElem = jQuery("[data-inview='true']");
var nextElem = inviewElem.next();
var prevElem = inviewElem.prev();
var currentTop = parseInt(firstElem.attr('data-top'));
if (e.originalEvent.detail > 0 || e.originalEvent.wheelDelta < 0) {
// Scrolling down
// if viewed element is last element do nothing
if (inviewElem.index() >= lastElem.index())
return false;
// if main section is inview then scroll through its elements
if (inviewElem.index() == mainElem.index()) {
// if the active child is not the last element then process
var active = jQuery('.text-container .active');
if (active.index() != textConainersElement.length - 1) {
jQuery('.text-container .active').removeClass('active').next().addClass('active');
// Dont scroll further
return false;
}
}
var top = currentTop + 100;
firstElem.css("margin-top", "-" + top + "vh").attr("data-top", top);
nextElem.attr("data-inview", 'true');
inviewElem.attr("data-inview", 'false');
} else {
// Scrolling up
// if viewed element is first element do nothing
if (inviewElem.index() <= firstElem.index())
return false;
// if main section is inview then scroll through its elements
if (inviewElem.index() == mainElem.index()) {
// if the active child is not the last element then process
var active = jQuery('.text-container .active');
if (active.index() != 0) {
jQuery('.text-container .active').removeClass('active').prev().addClass('active');
// Dont scroll further
return false;
}
}
var top = currentTop - 100;
firstElem.css("margin-top", "-" + top + "vh").attr("data-top", top);
prevElem.attr("data-inview", 'true');
inviewElem.attr("data-inview", 'false');
}
// Set values to use for next scrolling event
lastScrollPos = jQuery(window).scrollTop();
scrollFired = true;
// reset scrollFired var after transition ended
firstElem.one('transitionend', function() {
scrollFired = false;
});
//prevent page fom scrolling
return false;
});
body {
margin: 0;
}
.wrapper {
display: block;
width: 100%;
height: 100vh;
overflow: hidden;
}
section {
display: block;
width: 100%;
height: 100vh;
border-bottom: 2px dashed black;
position: relative;
transition: all 0.5s;
background-color: #c4c4c4;
}
section[data-inview="true"] {
background-color: #929292;
}
.phone-container {
align-items: center;
background: #dedede none repeat scroll 0 0;
border-left: 5px solid black;
display: flex;
float: right;
height: 100vh;
justify-content: center;
width: 500px;
}
.phone {
width: 200px;
height: 500px;
background: #A6A6A6 none repeat scroll 0 0;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
border-radius: 5px;
}
section.long-scroll {
height: auto;
}
p {
margin-top: 80px;
}
p:first-child {
margin-top: 0px;
}
.text-container {
float: left;
width: 200px;
}
.spacer {
display: block;
width: 100%;
}
p.active {
color: #C1E7FF;
}
.clearfix:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
.clearfix {
display: inline-block;
}
/* start commented backslash hack \*/
* html .clearfix {
height: 1%;
}
.clearfix {
display: block;
}
/* close commented backslash hack */
.stuck {
position: fixed;
top: 0;
}
.fixed {
position: fixed;
top: 0;
}
.sticky-wrapper {
height: auto !important;
}
.text-container {
padding-left: 40px;
padding-top: 40px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='wrapper'>
<section data-inview='true' data-top='0'>
Scroll-down
</section>
<section class="long-scroll clearfix" id="pin" data-main='true'>
<div class="text-container">
<p class="active">Text - 1</p>
<p>Text - 2</p>
<p>Text - 3</p>
<p>Text - 4</p>
</div>
<div class="phone-container">
<div class="phone">Slide-1</div>
</div>
</section>
<section id="unhook"></section>
</div>
Please add your function scroll_to_next() to $(window).scroll();
Since you are not capturing the scroll event provided by $(window). So it happens for the first time.
Example
I think the problem is with this line of code:
if (lastScrollTop < $(window).scrollTop() && lastScrollTop != 0)
Specifically
lastScrollTop != 0
The first time the code runs through the variable "lastScrollTop" is set to 0, so the if statement evaluates false and doesn't scroll, but the variables "shown_elements" and "next_element" are changed. The second time you run the code through "lastScrollTop" has been changed but it doesn't work as you would expect as the other variables have been changed.
The solution is probably to change the if statement and change "lastScrollTop != 0".
Related
The title says it all. To see the issue, copy this code to the following online compiler: https://www.w3schools.com/php/phptryit.asp?filename=tryphp_compiler
<!DOCTYPE HTML>
<html>
<style>
/*MAIN*/
* {
margin: 0;
padding: 0;
user-select: none;
overflow: hidden;
}
body {
background-color: #FF0000;
margin: 0;
padding: 0;
}
/*ELEMENTS*/
div {
width: 100vw;
height: 100vh;
float: left;
margin-left: 0vw;
}
h1 {
font-family: verdana;
font-size: 5vh;
text-transform: uppercase;
}
h1.white {
color: #F4F4F4;
}
</style>
<body>
<div id = "main" style = "width: auto; margin-left: 0vw;">
<div id = "home" class = "container" style = 'background-color: #000000;'>
<h1 class = "white">click arrow to see how the next page doesn't appear until after the transition is complete</h1>
<!--ARROW BUTTON-->
<p id = 'arrowButton' style = 'color: #FFFFFF; position: absolute; height: 10vh; width: auto; margin: 45vh 0 0 75vw; font-size: 3vh;' onMouseDown = 'NextButtonClick();'>--></p>
</div>
<div id = "welcome" class = "container" style = 'background-color: #FFFFFF;'>
<h1 style = 'margin: 47.5vh 0 0 50vw'>welcome to my portfolio</h1>
</div>
</div>
<script>
var mainDiv, welcomeDiv;
var transitionSeconds = 0.5;
var isTransitioning = false;
function NextButtonClick() {
if(!isTransitioning) {
isTransitioning = true;
i = 0;
thisInterval = setInterval(function() {
mainDiv.style.marginLeft = (100 / i) - 101 + "vw";
i++;
if(i == 100) {
clearInterval(thisInterval);
mainDiv.style.marginLeft = "-100vw";
isTransitioning = false;
}
}, transitionSeconds);
}
}
window.onload = function() {
mainDiv = document.getElementById("main");
welcomeDiv = document.getElementById("welcome");
var arrowButton = document.getElementById("arrowButton");
var arrowButtonX, arrowButtonY;
var arrowButtonGlowDistance = 100;
arrowButtonX = arrowButton.getBoundingClientRect().left + arrowButton.getBoundingClientRect().width/2;//center
arrowButtonY = arrowButton.getBoundingClientRect().top + arrowButton.getBoundingClientRect().height/2;//center
document.onmousemove = function(e) {
x = e.clientX; y = e.clientY;
};
};
</script>
</body>
</html>
The background is red on purpose so that you can see how, even though the "welcome" div should be rendered over top the background, it is not being rendered until the very last second after the transition is completed and 100% of the element is on the screen.
I am stumped, and I'm not sure why this is since HTML usually doesn't seem to behave this way. Even when I highlight the element in Inspect Element, the Inspector doesn't show me where the element is on the screen until the final moment when it is rendered.
Any help would be greatly appreciated, and I look forward to hearing your feedback!
The problem here is that your DIVs are placed under each other and while one is moving horizontally, the next div is still underneath of it until first one is completely out of the way (just like Jenga game in reverse).
To solve this, you can try add display: flex, to place them horizontally instead:
var mainDiv, welcomeDiv;
var transitionSeconds = 0.5;
var isTransitioning = false;
function NextButtonClick() {
if (!isTransitioning) {
isTransitioning = true;
i = 0;
thisInterval = setInterval(function() {
mainDiv.style.marginLeft = (100 / i) - 101 + "vw";
i++;
if (i == 100) {
clearInterval(thisInterval);
mainDiv.style.marginLeft = "-100vw";
isTransitioning = false;
}
}, transitionSeconds);
}
}
window.onload = function() {
mainDiv = document.getElementById("main");
welcomeDiv = document.getElementById("welcome");
var arrowButton = document.getElementById("arrowButton");
var arrowButtonX, arrowButtonY;
var arrowButtonGlowDistance = 100;
arrowButtonX = arrowButton.getBoundingClientRect().left + arrowButton.getBoundingClientRect().width / 2; //center
arrowButtonY = arrowButton.getBoundingClientRect().top + arrowButton.getBoundingClientRect().height / 2; //center
document.onmousemove = function(e) {
x = e.clientX;
y = e.clientY;
};
};
* {
margin: 0;
padding: 0;
user-select: none;
overflow: hidden;
}
body {
background-color: #FF0000;
margin: 0;
padding: 0;
}
/*ELEMENTS*/
div {
width: 100vw;
height: 100vh;
float: left;
margin-left: 0vw;
display: flex; /* added */
}
h1 {
font-family: verdana;
font-size: 5vh;
text-transform: uppercase;
}
h1.white {
color: #F4F4F4;
}
<div id="main" style="width: auto; margin-left: 0vw;">
<div id="home" class="container" style='background-color: #000000;'>
<h1 class="white">click arrow to see how the next page doesn't appear until after the transition is complete</h1>
<!--ARROW BUTTON-->
<p id='arrowButton' style='color: #FFFFFF; position: absolute; height: 10vh; width: auto; margin: 45vh 0 0 75vw; font-size: 3vh;' onMouseDown='NextButtonClick();'>--></p>
</div>
<div id="welcome" class="container" style='background-color: #FFFFFF;'>
<h1 style='margin: 47.5vh 0 0 50vw'>welcome to my portfolio</h1>
</div>
</div>
I am trying to make a class fade in when 'register' class div top enters the window and otherwise have the class fade out
How can I make this work?
$(window).scroll(function() {
var tint = $('.register').offset().top;
if ($(this).scrollTop() > tint) {
$('.tint').fadeIn(1000);
} else {
$('.tint').fadeOut(1000);
}
});
section {
height: 100vh;
width: 100%;
}
.tint {
background-color: #000;
width: 100%;
height: 100vh;
opacity: 0.6;
position: absolute;
top: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section></section>
<section class="register">
<div class="tint"></div>
<div>Content</div>
</section>
Class example code you posted seems to work correctly, I think you should check css also if there is any problem. But if want any other methods then here are some methods: you can use fadIn() - fadeOut(), or hide() - show() or css visibility or display block and none , or simply can jquery UI animate functionality. Here is animation example to hide div jquery animate
$(window).scroll(function() {
var top_of_element = $("#element").offset().top;
var bottom_of_element = $("#element").offset().top + $("#element").outerHeight();
var bottom_of_screen = $(window).scrollTop() + $(window).innerHeight();
var top_of_screen = $(window).scrollTop();
if ((bottom_of_screen > top_of_element) && (top_of_screen < bottom_of_element)){
// the element is visible, do something
} else {
// the element is not visible, do something else
}
});
$(window).scroll(function() {
var tint = $('.register').offset().top;
if ($(this).scrollTop() > tint) {
$('.register').fadeIn();
// OR
//$(".register").show();
// OR
//$(".register").css('visibility', 'visible');
} else {
$('.register').fadeOut();
// OR
//$(".register").hide();
// OR
//$(".register").css('visibility', 'hidden');
}
});
.tint {
background-color: #000;
width: 100%;
height: 100vh;
opacity: 0.6;
position: absolute;
top: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section class="register">
<div class="tint"></div>
<div>Content</div>
</section>
The only problem, that you've passed a string to fadeOut, which expects a number.
So, remove the quotes around '1000', and you'll be right:
$(window).scroll(function() {
var tint = $('.register').offset().top;
if ($(this).scrollTop() > tint) {
$('.tint').fadeIn(1000);
} else {
$('.tint').fadeOut(1000);
}
});
section {
height: 100vh;
width: 100%;
}
.tint {
background-color: #000;
width: 100%;
height: 100vh;
opacity: 0.6;
position: absolute;
top: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section></section>
<section class="register">
<div class="tint"></div>
<div>Content</div>
</section>
What I trying to do is, show .box-tocart when scroll top bigger than .product-info-main offset top and also if reached to .page-footer should hide but I couldn't mix these conditions together, each condition work separately but not working together with || or &&
var target = $('.product-info-main').offset().top;
$(window).scroll(function() {
var footer = $('.page-footer').offset().top;
var element = $('.box-tocart').offset().top;
if (($(window).scrollTop() >= target) || (element >= footer)) {
$('.box-tocart').show();
} else {
$('.box-tocart').hide();
}
});
body {
height: 2000px;
}
#nothing {
height: 100px;
background: red;
}
.product-info-main {
height: 1000px;
}
.box-tocart {
height: 30px;
background: green;
display: none;
position: fixed;
bottom: 0;
left: 0;
width: 100%;
}
.page-footer {
background: blue;
height: 100px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="nothing"></div>
<div class="product-info-main">
<div class="box-tocart"></div>
</div>
<div class="page-footer"></div>
Goal: show .box-tocart if scroll top bigger than .product-info-main offset top, else hide. Also if reached to .page-footer hide, else show, but I want these two conditions together, but couldn't make it work.
The problem with current snippet is, it not hide .box-tocart after reach .page-footer
Simple explanation: green div should show after red div, else hide and should hide after
reach to blue div else hide.
You need to change the condition to:
var scrollTop = $(window).scrollTop();
var windowHeight = $(window).height();
if ((scrollTop >= target) && (scrollTop + windowHeight <= footer)) {
// ...
}
Updated example:
var target = $('.product-info-main').offset().top;
$(window).scroll(function() {
var footer = $('.page-footer').offset().top;
var element = $('.box-tocart').offset().top;
var scrollTop = $(window).scrollTop();
var windowHeight = $(window).height();
if ((scrollTop >= target) && (scrollTop + windowHeight <= footer)) {
$('.box-tocart').show();
} else {
$('.box-tocart').hide();
}
});
body {
height: 2000px;
}
#nothing {
height: 100px;
background: red;
}
.product-info-main {
height: 1000px;
}
.box-tocart {
height: 30px;
background: green;
display: none;
position: fixed;
bottom: 0;
left: 0;
width: 100%;
}
.page-footer {
background: blue;
height: 100px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="nothing"></div>
<div class="product-info-main">
<div class="box-tocart"></div>
</div>
<div class="page-footer"></div>
You should use $(window).scrollTop() instead of element so your OR condition should be like if (... || $(window).scrollTop() >= footer, that’s because the scroll position is all relative to the window view and not to the cart box
I hope it can help you.
I'm trying to create a seamless looping webpage where the content repeats over and over without having to actually duplicate the divs. I came across a THIS CODEPEN, which is exactly what I was looking for, but the mobile experience is really glitchy and flickers when repositioning to the top of the page.
Any solves for mobile? Or edits to this JS?
See below and thanks!
var doc = window.document,
context = doc.querySelector('.js-loop'),
clones = context.querySelectorAll('.is-clone'),
disableScroll = false,
scrollHeight = 0,
scrollPos = 0,
clonesHeight = 0,
i = 0;
function getScrollPos () {
return (context.pageYOffset || context.scrollTop) - (context.clientTop || 0);
}
function setScrollPos (pos) {
context.scrollTop = pos;
}
function getClonesHeight () {
clonesHeight = 0;
for (i = 0; i < clones.length; i += 1) {
clonesHeight = clonesHeight + clones[i].offsetHeight;
}
return clonesHeight;
}
function reCalc () {
scrollPos = getScrollPos();
scrollHeight = context.scrollHeight;
clonesHeight = getClonesHeight();
if (scrollPos <= 0) {
setScrollPos(1); // Scroll 1 pixel to allow upwards scrolling
}
}
function scrollUpdate () {
if (!disableScroll) {
scrollPos = getScrollPos();
if (clonesHeight + scrollPos >= scrollHeight) {
// Scroll to the top when you’ve reached the bottom
setScrollPos(1); // Scroll down 1 pixel to allow upwards scrolling
disableScroll = true;
} else if (scrollPos <= 0) {
// Scroll to the bottom when you reach the top
setScrollPos(scrollHeight - clonesHeight);
disableScroll = true;
}
}
if (disableScroll) {
// Disable scroll-jumping for a short time to avoid flickering
window.setTimeout(function () {
disableScroll = false;
}, 40);
}
}
window.requestAnimationFrame(reCalc);
context.addEventListener('scroll', function () {
window.requestAnimationFrame(scrollUpdate);
}, false);
window.addEventListener('resize', function () {
window.requestAnimationFrame(reCalc);
}, false);
// Just for this demo: Center the middle block on page load
window.onload = function () {
setScrollPos(Math.round(clones[0].getBoundingClientRect().top + getScrollPos() - (context.offsetHeight - clones[0].offsetHeight) / 1));
};
html,
body {
height: 100%;
overflow: hidden;
}
.Loop {
position: relative;
height: 100%;
overflow: auto;
-webkit-overflow-scrolling: touch;
}
section {
position: relative;
text-align: center;
min-height: 100vh;
max-height: 100vh;
height: 100%;
}
::scrollbar {
display: none;
}
body {
font-family: "Avenir Next", Helvetica, sans-serif;
font-weight: normal;
font-size: 100%;
}
.red {
background: #FF4136;
}
.green {
background: #2ECC40;
}
.blue {
background: #0074D9;
}
.orange {
background: rebeccapurple;
}
h1 {
margin: 0;
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 100%;
font-size: 80px;
letter-spacing: 5px;
color: #fff;
text-transform: uppercase;
}
<main class="Loop js-loop">
<section class="green">
<h1>FIRST</h1>
</section>
<section class="red">
<h1>For</h1>
</section>
<section class="blue">
<h1>All</h1>
</section>
<section class="orange">
<h1>And</h1>
</section>
<section class="blue">
<h1>All</h1>
</section>
<section class="red">
<h1>LAST</h1>
</section>
<!--
These blocks are the same as the first blocks to get that looping illusion going.
You need to add clones to fill out a full viewport height.
-->
<section class="green is-clone">
<h1>FIRST</h1>
</section>
<section class="red is-clone">
<h1>For</h1>
</section>
</main>
I was trying to move the divs (here it's question number) based on the prev and next button. So that the selected question is always visible on screen.
Here is the demo : http://jsfiddle.net/arunslb123/trxe4n3u/12/
Screen :
click and question number and click prev or next button to understand my issue.
My code :
$("#next")
.click(function () {
$(".c.current-question")
.each(function () {
var divIdx = $(this)
.attr('id');
var scrollTo = $('#' + divIdx)
.position()
.left;
$("#scrollquestion")
.animate({
'scrollLeft': scrollTo
}, 800);
});
});
$("#prev")
.click(function () {
$(".c.current-question")
.each(function () {
var divIdx = $(this)
.attr('id');
var scrollTo = $('#' + divIdx)
.position()
.left;
$("#scrollquestion")
.animate({
'scrollLeft': -scrollTo
}, 800);
});
});
Using scrollLeft is a bit tricky. I did a small redo of your use-case based on positioning and then moving it based on left of the container. The tricky part is to reliably calculate the negative position when scrolled to the extreme right. Also, need to take into account the widths and margins.
Check the below snippet:
var $wrap = $("#numWrap"), $strip = $("#strip"),
$leftArrow = $(".wrapper > .arrows").first(),
wrapWidth = $wrap.width() + $leftArrow.width(),
margin = 10;
fill(20); select($(".numberItem").first());
$strip.on("click", ".numberItem", function() { select($(this)); });
function select($elem) {
$(".numberItem").removeClass("selected");
$elem.addClass("visited").addClass("selected");
focus($elem[0]);
}
function focus(elem) {
var stripPos = $strip.position(),
numPos = $(elem).offset(),
elemWidth = $(elem).width() + margin,
numRight = numPos.left + elemWidth;
if (numRight > wrapWidth) {
$strip.css({"left": stripPos.left - elemWidth});
}
if (numPos.left < (margin + $leftArrow.width())) {
$strip.css({"left": stripPos.left + elemWidth});
}
}
$(".wrapper").on("click", "a.arrow", function() {
var stripPos = $strip.position();
if (this.id == "lft") {
$strip.css({"left": stripPos.left + (wrapWidth / 2)});
} else {
$strip.css({"left": stripPos.left - (wrapWidth / 2)});
}
});
$(".controls").on("click", "a.arrow", function() {
var $sel = $(".selected"), numPos, $sel, elemWidth;
$elem = $sel.length > 0 ? $sel.first() : $(".numberItem").first();
if (this.id == "lft") {
$sel = $elem.prev().length > 0 ? $elem.prev() : $elem;
select($sel);
} else {
$sel = $elem.next().length > 0 ? $elem.next() : $elem;
select($sel);
}
numPos = $sel.offset(); elemWidth = $sel.width() + margin;
numRight = numPos.left + elemWidth;
if (numPos.left > wrapWidth) {
$strip.css({"left": -($sel.text()) * $sel.width() });
}
if (numRight < 0) {
$strip.css({"left": +($sel.text()) * $sel.width() });
}
});
function fill(num){
for (var i = 1; i <= num; i++) {
var $d = $("<a href='#' class='numberItem'>" + i + "</a>");
$strip.append($d);
}
}
* { box-sizing: border-box; padding: 0; margin: 0; font-family: sans-serif; }
div.wrapper {
background-color: #ddd; width: 100vw; height: 64px;
clear: both; overflow: hidden; margin-top: 16px;
}
div.arrows {
float: left; width: 10%; min-width: 24px; height: 64px; line-height: 64px;
text-align: center; vertical-align: middle; overflow: hidden;
}
div.numWrap {
float: left; height: 64px; line-height: 64px;
width: 80%; vertical-align: middle;
overflow: hidden; position: relative;
}
div.strip {
position: absolute; left: 0px;
width: auto; white-space: nowrap;
transition: left 1s;
}
a.numberItem {
display: inline-block; text-align: center; margin: 0px 8px;
background-color: #fff; border-radius: 50%; width: 48px; height: 48px;
font-size: 1.2em; line-height: 48px; text-decoration: none;
}
a.numberItem.visited { background-color: #fff; color: #000; border: 2px solid #01aebc; }
a.numberItem.selected { background-color: #01aebc; color: #fff; }
div.controls { clear: both; }
div.controls > div.arrows { width: auto; margin: 0 12px; }
a, a:focus, a:active, a:link, a:visited {
display: inline-block;
text-decoration: none; font-weight: 600;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
<div class="arrows">
<a id="lft" class="arrow" href="#">〈</a>
</div>
<div id="numWrap" class="numWrap">
<div id="strip" class="strip"></div>
</div>
<div class="arrows">
<a id="rgt" class="arrow" href="#">〉</a>
</div>
</div>
<div class="controls">
<div class="arrows">
<a id="lft" class="arrow" href="#">〈 Previous</a>
</div>
<div class="arrows">
<a id="rgt" class="arrow" href="#">Next 〉</a>
</div>
<div>
Explanation:
Using absolute positioning on the number container, which is nested to get 100% width.
Markup:
<div class="wrapper">
<div class="arrows"><a id="lft" class="arrow" href="#">〈</a></div>
<div id="numWrap" class="numWrap">
<div id="strip" class="strip"></div> <!-- nesting here -->
</div>
<div class="arrows"><a id="rgt" class="arrow" href="#">〉</a></div>
</div>
CSS:
div.wrapper {
background-color: #ddd; width: 100vw; height: 64px;
clear: both; overflow: hidden; margin-top: 16px;
}
div.arrows {
float: left; width: 10%; min-width: 24px; height: 64px; line-height: 64px;
text-align: center; vertical-align: middle; overflow: hidden;
}
div.numWrap {
float: left; height: 64px; line-height: 64px;
width: 80%; vertical-align: middle;
overflow: hidden; position: relative; /* relatively positioned */
}
div.strip {
position: absolute; left: 0px; /* absolutely positioned */
width: auto; white-space: nowrap;
transition: left 1s; /* instead of jquery animate */
}
With this structure, we can now use left to control the scrolling.
For partially obscured numbers, try to gently focus-in (nudge into view) a number which is partially obscured. This can be done by checking the position relative to parent and adding the width/margin to it and also accounting for width of the left arrow (it might peep thru).
Javascript:
function focus(elem) {
var stripPos = $strip.position(),
numPos = $(elem).offset(),
elemWidth = $(elem).width() + margin,
numRight = numPos.left + elemWidth;
// if it is towards right side, nudge it back inside
if (numRight > wrapWidth) {
$strip.css({"left": stripPos.left - elemWidth});
}
// if it is towards left side, nudge it back inside
if (numPos.left < (margin + $leftArrow.width())) {
$strip.css({"left": stripPos.left + elemWidth});
}
}
Once the user has scrolled the list too far and then tries to click on previous / next buttons to select a question, then we need to move the entire container upto the selected number. We can easily do this by multiplying the question number with element width and then changing the left in positive (if towards right) or in negative (if towards left).
Javascript:
// if left of element is more than the width of parent
if (numPos.left > wrapWidth) {
$strip.css({"left": -($sel.text()) * $sel.width() });
}
// if right of element is less than 0 i.e. starting position
if (numRight < 0) {
$strip.css({"left": +($sel.text()) * $sel.width() });
}
Here is a fiddle to play with: http://jsfiddle.net/abhitalks/aw166qhx/
You will need to further adapt it to your use-case, but you get the idea.