How to keep user's scrolling place when resizing div - javascript

I wanted to do a cool menu effect for a website I'm working on. I'm having a div act as the the section for the main content. When the user opens the menu, the main content div will resize and move out of the way, revealing the menu. However, when I do this with the code I have written, it always loses my scrolling place on the page. Is there any way to keep my place on the page when it shrinks and also when it expands back again? Below is what I have. Thank you in advance!
function shrinkPage() {
var element = document.getElementById("mock-body");
element.classList.toggle("mock-body-on-burger");
var z = document.getElementById("mock-body-container");
z.classList.toggle("mock-body-container-on-burger");
var x = document.getElementById("body");
x.classList.toggle("body-on-burger");
};
body {
margin: 0;
background:#000;
}
.body-on-burger {
max-width: 100%;
overflow-x:hidden;
}
.mock-body-container{
height:100vh;
}
.mock-body-container-on-burger {
height:100vh;
transform: scale(0.4) translate(130%);
overflow: hidden;
}
.mock-body-size-change{
overflow: scroll;
}
.mock-body {
position:relative;
background: #fff;
margin-left: 50px;
}
.container {
position: fixed;
height:50px;
width:50px;
cursor: pointer;
}
.container #icon {
width: 16px;
height: 8px;
position: relative;
margin: 0px auto 0;
top: 40%;
}
.container #icon .bars {
height: 1px;
background: #fff;
}
.myDiv {
height:500px;
}
.one {
background:red;
}
.two {
background:green;
}
.three {
background:blue;
}
<body id="body">
<div class="menu-activator" onclick="shrinkPage()">
<div class="container usd">
<div id="icon">
<div class="bars first"></div>
</div>
</div>
</div>
<div id="mock-body-container" class="mock-body-container">
<div id="mock-body" class="mock-body">
<div class="myDiv one"></div>
<div class="myDiv two"></div>
<div class="myDiv three"></div>
</div>
</div>
</body>

Please take a look at the snippet below. Notice how the overflow property is used.
You have to scroll mock-body-container to keep its scrolling position.
You're scrolling body instead, so when you scale mock-body-container there is nothing to scroll in body and you loose the scrolling position.
function shrinkPage() {
var element = document.getElementById("mock-body");
element.classList.toggle("mock-body-on-burger");
var z = document.getElementById("mock-body-container");
z.classList.toggle("mock-body-container-on-burger");
var x = document.getElementById("body");
x.classList.toggle("body-on-burger");
};
body {
margin: 0;
background:#000;
}
.body-on-burger {
max-width: 100%;
overflow-x:hidden;
}
.mock-body-container{
height:100vh;
overflow:auto;
}
.mock-body-container-on-burger {
height:100vh;
transform: scale(0.4) translate(130%);
}
.mock-body-size-change{
overflow: scroll;
}
.mock-body {
position:relative;
background: #fff;
margin-left: 50px;
}
.container {
position: fixed;
height:50px;
width:50px;
cursor: pointer;
}
.container #icon {
width: 16px;
height: 8px;
position: relative;
margin: 0px auto 0;
top: 40%;
}
.container #icon .bars {
height: 1px;
background: #fff;
}
.myDiv {
height:500px;
}
.one {
background:red;
}
.two {
background:green;
}
.three {
background:blue;
}
<body id="body">
<div class="menu-activator" onclick="shrinkPage()">
<div class="container usd">
<div id="icon">
<div class="bars first"></div>
</div>
</div>
</div>
<div id="mock-body-container" class="mock-body-container">
<div id="mock-body" class="mock-body">
<div class="myDiv one"></div>
<div class="myDiv two"></div>
<div class="myDiv three"></div>
</div>
</div>
</body>

Once you know the element that was in focus it should be relatively easy. If you need to find which element was last in focus, you can do that with a scroll function. If you need this as well let me know and I will update my answer.
If you know that #mock-body is the last element in focus, just scroll back to it after the resize.
In this example I've used jQuery as it makes this interaction easier, but this can be done (albeit more verbosely) with vanilla JS as well.
$('html, body').animate({
scrollTop: $('#mock-body').offset().top
}, 0); // If you want the animation to be smoother you can increase 0 to a higher number

A simple way to do it is to remember the position of the document scroll and reapply it when you getting back to "normal" view:
let savedScroll;
function shrinkPage() {
let _s = (el) => document.querySelector(el),
s_ = (d) => !d.classList.contains('body-on-burger'),
x = _s('#body'),
element = _s('#mock-body'),
z = _s('#mock-body-container');
if (s_(x)) {
savedScroll = document.documentElement.scrollTop;
}
element.classList.toggle("mock-body-on-burger");
z.classList.toggle("mock-body-container-on-burger");
x.classList.toggle("body-on-burger");
if (s_(x)) {
document.documentElement.scrollTop = savedScroll;
}
};
Check it out:
let savedScroll;
function shrinkPage() {
let _s = (el) => document.querySelector(el),
s_ = (d) => !d.classList.contains('body-on-burger'),
x = _s('#body'),
element = _s('#mock-body'),
z = _s('#mock-body-container');
if (s_(x)) {
savedScroll = document.documentElement.scrollTop;
}
element.classList.toggle("mock-body-on-burger");
z.classList.toggle("mock-body-container-on-burger");
x.classList.toggle("body-on-burger");
if (s_(x)) {
document.documentElement.scrollTop = savedScroll;
}
};
body {
margin: 0;
background: #000;
}
.body-on-burger {
max-width: 100%;
overflow-x: hidden;
}
.mock-body-container {
height: 100vh;
}
.mock-body-container-on-burger {
height: 100vh;
transform: scale(0.4) translate(130%);
overflow: hidden;
}
.mock-body-size-change {
overflow: scroll;
}
.mock-body {
position: relative;
background: #fff;
margin-left: 50px;
}
.container {
position: fixed;
height: 50px;
width: 50px;
cursor: pointer;
}
.container #icon {
width: 16px;
height: 8px;
position: relative;
margin: 0px auto 0;
top: 40%;
}
.container #icon .bars {
height: 1px;
background: #fff;
}
.myDiv {
height: 500px;
}
.one {
background: red;
}
.two {
background: green;
}
.three {
background: blue;
}
<body id="body">
<div class="menu-activator" onclick="shrinkPage()">
<div class="container usd">
<div id="icon">
<div class="bars first"></div>
</div>
</div>
</div>
<div id="mock-body-container" class="mock-body-container">
<div id="mock-body" class="mock-body">
<div class="myDiv one"></div>
<div class="myDiv two"></div>
<div class="myDiv three"></div>
</div>
</div>
</body>
Legend: _s(el) returns first match of el and s_(d) checks if d has class body-on-burger.

The simple way to do this is to determine the change in height during the resize, and scroll that much.
const heightChange = newHeight - initialHeight;
scrollableDiv.scrollTop = scrollableDiv.scrollTop - heightChange;
In my case I am using a resize method I wrote, so I do this work inside of a window.addEventListener("mousemove", handleResize); when I know the div in actively being resized by the user.
This will still work fine with native html resizable elements, you just need to figure out how/when to listen for resize/drag events accordingly.

Related

Show/hide div or image when scrolling

Is there a way to show an image or a div when scrolling down a web page and hide it when not scrolling and vice versa?
So in the code below the red div would be displayed when not scrolling, and the green div would be displayed only when scrolling.
.square {
position: fixed;
width: 100px;
height: 100px;
}
.green {
background: green;
display: none;
}
.red {
background: red;
}
.container {
width: 100%;
height: 3000px;
}
<div class="container">
<div class="square green"></div>
<div class="square red"></div>
</div>
The end goal is to achieve something like this: https://mailchimp.com/annual-report/ where the character appears to be walking when the user scrolls, and stands still when the user stops. Is this easily achievable?
You just need an eventListener that listen to a scroll event. However this has the issue that it only recoginze when you scroll but not when you stop scrolling. For that you can use this answer that explains how to listen for a "scroll-stop"
To make the code shorter and easier, I removed your display: none from the green box. I added a new class d-none that contains this proeprty now instead. By default it is added to the green box.
With classList.toggle('d-none') I can toggle class within both boxes which makes it easier then to address and then add or remove the class for every box on its own.
var timer = null;
var box = document.querySelectorAll('.square');
window.addEventListener('scroll', function() {
if (timer !== null) {
clearTimeout(timer);
} else {
box.forEach(el => el.classList.toggle('d-none'));
}
timer = setTimeout(function() {
box.forEach(el => el.classList.toggle('d-none'));
}, 150);
}, false);
.d-none {
display: none;
}
.square {
position: fixed;
width: 100px;
height: 100px;
}
.green {
background: green;
/* display: none; */
/* removed */
}
.red {
background: red;
}
.container {
width: 100%;
height: 3000px;
}
<div class="container">
<div class="square green d-none"></div>
<div class="square red"></div>
</div>
You just need a setTimeout function:
(function($) {
$(function() {
$(window).scroll(function() {
$('.square.red').show()
$('.square.green').hide()
clearTimeout($.data(this));
$.data(this, setTimeout(function() {
$('.square.red').hide()
$('.square.green').show()
}, 250));
});
});
})(jQuery);
.square {
position: fixed;
width: 100px;
height: 100px;
}
.green {
background: green;
}
.red {
background: red;
display: none;
}
.container {
width: 100%;
height: 3000px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="square green"></div>
<div class="square red"></div>
</div>

How to scroll to next div using Javascript?

So I'm making a website with a lot of Divs that take 100% height.
And I want to make a button so when it's clicked to smoothly scroll to next div.
I've coded something so when its clicked, it scrolls to specific div.
$(".next").click(function() {
$('html,body').animate({
scrollTop: $(".p2").offset().top},
'slow');
});
body{
margin: 0;
height: 100%;
}
.p1{
height: 100vh;
width: 70%;
background-color: #2196F3;
}
.p2{
height: 100vh;
width: 70%;
background-color: #E91E63;
}
.p3{
height: 100vh;
width: 70%;
background-color: #01579B;
}
.admin{
background-color: #B71C1C;
height: 100vh;
position: fixed;
right: 0%;
top: 0%;
width: 30%;
float: left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="p1">
</div>
<div class="p2">
</div>
<div class="p3">
</div>
<div class="admin">
<button class="next">NEXT</button>
</div>
To make this work you need to identify the currently displayed div. For that you can apply a class to the element which is currently shown. Then you can use next() to traverse through them all.
Also note in the below example the addition of a common class on all elements, .p, in order to DRY up the CSS and make DOM traversal easier.
$(".next").click(function() {
var $target = $('.p.active').next('.p');
if ($target.length == 0)
$target = $('.p:first');
$('html, body').animate({
scrollTop: $target.offset().top
}, 'slow');
$('.active').removeClass('active');
$target.addClass('active');
});
body {
margin: 0;
height: 100%;
}
.p {
height: 100vh;
width: 70%;
}
.p1 {
background-color: #2196F3;
}
.p2 {
background-color: #E91E63;
}
.p3 {
background-color: #01579B;
}
.admin {
background-color: #B71C1C;
height: 100vh;
position: fixed;
right: 0%;
top: 0%;
width: 30%;
float: left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="p p1 active"></div>
<div class="p p2"></div>
<div class="p p3"></div>
<div class="admin">
<button class="next">NEXT</button>
</div>
Use same class name for container.Start with first element.Each time click target the next scroller element
var f = $('.p1');
var nxt = f;
$(".next").click(function() {
if (nxt.next('.scroller').length > 0) {
nxt = nxt.next('.scroller');
} else {
nxt = f;
}
$('html,body').animate({
scrollTop: nxt.offset().top
},
'slow');
});
body {
margin: 0;
height: 100%;
}
.p1 {
height: 100vh;
width: 70%;
background-color: #2196F3;
}
.p2 {
height: 100vh;
width: 70%;
background-color: #E91E63;
}
.p3 {
height: 100vh;
width: 70%;
background-color: #01579B;
}
.admin {
background-color: #B71C1C;
height: 100vh;
position: fixed;
right: 0%;
top: 0%;
width: 30%;
float: left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="p1 scroller">
</div>
<div class="p2 scroller">
</div>
<div class="p3 scroller">
</div>
<div class="admin">
<button class="next">NEXT</button>
</div>
Here is a basic version that moves forward and wraps around to the beginning when it reaches the last slide. We store currSlide outside of the loop and increment the number internally in the function.
For convenience, I added a slide class to each slide which allows me to:
easily count the length of the slides
condense the CSS
let currSlide = 1;
const SLIDE_LENGTH = $('.slide').length;
$(".next").click(function() {
currSlide = currSlide === SLIDE_LENGTH ? 1 : ++currSlide;
$('html,body').animate({
scrollTop: $(`.p${currSlide}`).offset().top
},
'slow');
});
body {
margin: 0;
height: 100%;
}
/* Less repetition */
.slide {
height: 100vh;
width: 70%;
}
.p1 {
background-color: #2196F3;
}
.p2 {
background-color: #E91E63;
}
.p3 {
background-color: #01579B;
}
.admin {
background-color: #B71C1C;
height: 100vh;
position: fixed;
right: 0%;
top: 0%;
width: 30%;
float: left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="slide p1"></div>
<div class="slide p2"></div>
<div class="slide p3"></div>
<div class="admin">
<button class="next">NEXT</button>
</div>
jsFiddle
Bonus edit:
In case you're interested in adding a previous button at some point…
let currSlide = 1;
const SLIDE_LENGTH = $('.slide').length;
function moveSlide() {
currSlide = $(this).hasClass("next") ? ++currSlide : --currSlide;
if (currSlide < 1) {
currSlide = SLIDE_LENGTH;
}
if (currSlide > SLIDE_LENGTH) {
currSlide = 1;
}
$('html,body').animate({
scrollTop: $(`.p${currSlide}`).offset().top
},
'slow');
}
$(".prev, .next").on("click", moveSlide);
body {
margin: 0;
height: 100%;
}
/* Less repetition */
.slide {
height: 100vh;
width: 70%;
}
.p1 {
background-color: #2196F3;
}
.p2 {
background-color: #E91E63;
}
.p3 {
background-color: #01579B;
}
.admin {
background-color: #B71C1C;
height: 100vh;
position: fixed;
right: 0%;
top: 0%;
width: 30%;
float: left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="slide p1"></div>
<div class="slide p2"></div>
<div class="slide p3"></div>
<div class="admin">
<button class="prev">PREVIOUS</button>
<button class="next">NEXT</button>
</div>
jsFiddle
Since your question asks "How-to in Javascript", I will answer the question in a few lines of vanilla JS:
var p = 1;
const container = document.getElementById('container');
var nextPage = function() {
var topPos = document.getElementsByClassName('page')[p++].offsetTop;
container.scrollTo({top: topPos, behavior: 'smooth'});
}
in the above example, page is the class name you assign to your divs you want to scroll to, such as:
<div id="container">
<div class="page p1"></div>
<div class="page p2"></div>
<div class="page p3"></div>
</div>
Since you want to scroll the entire browser window, you just replace
container.scrollTo({top: topPos, behavior: 'smooth'});
with
window.scrollTo({top: topPos, behavior: 'smooth'});
like so:
var p = 1;
var nextPage = function() {
var topPos = document.getElementsByClassName('page')[p++].offsetTop;
window.scrollTo({top: topPos, behavior: 'smooth'});
}
You can subtract or add the number of pixels you want to offset the topPos if it's not in the right position

align divs passed the browsers width with a slide in effect

How can I align the following coloured divs next to each other, so that each one is the full width of the browser window (responsive) and only one is display at a time?
http://codepen.io/anon/pen/gpxvjm
HTML:
<div class="content-area">
<div class="p1">Page 1</div>
<div class="p2">Page 2</div>
<div class="p3">Page 3</div>
</div>
<a class="previous-page" href="#">Previous Page</a>
<a class="next-page"href="#">Next Page</a>
CSS:
body {background: grey;}
.p1 {
background: red;
float: left;
width: 100%;
display: inline;
overflow: hidden;
}
.p2 {
background: blue;
float: left;
width: 100%;
display: inline;
overflow: hidden;
}
.p3 {
background: green;
float: left;
width: 100%;
display: inline;
overflow: hidden;
}
.content-area {
width: 100%;
overflow: hidden;
}
Also, when the user selects the previous and next buttons, how can I then push the divs either left or right depending on which anchor link is selected?
I would really recommend slick.js to create the carousel effect you are after.
It is super easy to setup, allows for responsive design, is quite compatible with old IE and has features like arrows and dots built in!
If you want to run your own, i would suggest toggling classes in javascript which leverage css transforms & transitions to ensure maximum performance.
For example,
.page{
width: 100%;
height: 500px;
position: absolute;
top: 0;
transition: .5s;
}
.hide-left{
transform: translateX(-100%);
}
.hide-right{
transform: translateX(100%);
}
Adding browser specific prefixes where required.
This is one of solution, define parent with (number of pages) x 100% width and each child of page 100% / (number of pages) width :
var currentPage = 0;
$('.previous-page').on('click', function() {
currentPage--;
$('.content-area').css('left', (currentPage * -100) + '%');
});
$('.next-page').on('click', function() {
currentPage++;
$('.content-area').css('left', (currentPage * -100) + '%');
});
body {background: grey;}
.content-area div {
float: left;
width: 33.3333%;
display: inline;
overflow: hidden;
}
.p1 {
background: red;
}
.wrap {
width: 100%;
position: relative;
overflow: hidden;
}
.content-area {
width: 300%;
overflow: hidden;
left: 0;
position: relative;
-webkit-transition: left 2s;
transition: left 2s;
}
.p2 {
background: blue;
}
.p3 {
background: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrap">
<div class="content-area">
<div class="p1">Page 1</div>
<div class="p2">Page 2</div>
<div class="p3">Page 3</div>
</div>
</div>
<a class="previous-page" href="#">Previous Page</a>
<a class="next-page"href="#">Next Page</a>
You can improve the script, i just made it to illustrate the slider.
Here, I've created a jsfiddle here for what I think it is that you want. I've also created a snippet below.
$(document).ready(function () {
for (var i = 0; i < $(".content-area div").length; i++) {
$(".content-area div").eq(i).css({
left: "+=" + parseInt($(".content-area").width() * i, 10)
});
}
});
var currentPage = 0; //0th index
$(".previous-page").click(function () {
changePage(-1);
});
$(".next-page").click(function () {
changePage(1);
});
function changePage(updown) {
if ($(".content-area div")[currentPage + updown]) {
$(".content-area div").animate({
left: "+=" + parseInt($(".content-area").width() * -updown, 10)
},1600);
currentPage += updown;
}
}
body, html {
background: grey;
margin: 0;
padding: 0;
}
.p1 {
background: red;
}
.p2 {
background: blue;
}
.p3 {
background: green;
}
.content-area {
position: absolute;
left:0;
right:0;
top:0;
bottom:0;
overflow: hidden;
}
.content-area div {
position:absolute;
left:0;
width: 100%;
height:100%;
}
.next-page, .previous-page {
z-index:2;
position:fixed;
bottom:5px;
}
.next-page {
right:5px;
}
.previous-page {
left:5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="content-area">
<div class="p1">Page 1</div>
<div class="p2">Page 2</div>
<div class="p3">Page 3</div>
</div>
<a class="previous-page" href="#">Previous Page</a>
<a class="next-page" href="#">Next Page</a>

How to remove hover state when the element moves

I have this example:
<div class="container">
<div class="bp"></div>
</div>
http://jsfiddle.net/VKwjD/20/
if you hover the same square, his color changes; if you click, the parent size changes so the square move.
My problem is: If you don't move your mouse after the click, the square stays in the hover state... Why ?
It's possible to remove this state after the click? without moving the mouse...
Thank you for your help
HTML:
<div class="container">
<div class="bp" id="bp"></div>
</div>
CSS:
.container {
background: #FF0000;
width: 200px;
height: 200px;
position: relative;
}
.container.hide {
width: 50px;
}
.bp {
background: #00FFFF;
width: 30px;
height:30px;
top:0px;
right:0px;
position:absolute;
cursor: pointer;
}
.bp:hover{
background: #0000FF!important;
}
JavaScript:
$(document).ready(function() {
$('.bp').click(function() {
if($('.container').hasClass('hide')) {
$('.container').removeClass('hide');
var l1 = document.getElementById('bp');
l1.style.background = '#00FFFF';
}
else {
$('.container').addClass('hide');
var l1 = document.getElementById('bp');
l1.style.background = '#0000FF';
}
});
});

CSS3 or JAVASCRIPT for hover

I would like to put in evidence a picture (and blur all the rest) when I am over a link. Here my Html:
<body>
<div id="back">
<div id="one"></div>
<div id="two"></div>
</div>
<div id="menu">
one</div>
two</div>
</div>
</body>
and CSS:
#Back{
position: absolue;
background-image: url(images/fond.png);
width: 960px;
height: 600px;
margin: 0 auto;
}
#one{
background-image: url(images/formation.png);
width: 960px;
height: 600px;
z-index:1;
}
#two{
background-image: url(images/experiences.png);
width: 960px;
height: 600px;
z-index:2;
margin-top:-600px;
}
The problem i tried in css with this:
#link1:hover #one{
display:none;
}
And in javascript with this script:
function over(id){
if(document.getElementById(id)){
var objet = document.getElementById(id);
objet.style.display = "none";
}
}
Both doesn t work. I m not super good with the javascript. Thank so much for your help!!!
HTML:
<div id="menu">
link1
link2
</div>
<div class="div0" id="zero">
<div class="div1" id="one"></div>
<div class="div2" id="two"></div>
</div>
CSS:
.div0 {
position: absolute;
top: 30px;
left: 0px;
background-image: url(http://www.sanbarcomputing.com/images/js.jpg);
background-size: 400px 400px;
width: 400px;
height: 400px;
transition: 1s;
}
.div1 {
position: absolute;
top: 30px;
left: 0px;
background-image: url(http://www.sanbarcomputing.com/images/html5-logo.png);
background-size: 200px 200px;
width: 200px;
height: 200px;
transition: 1s;
}
.div2 {
position: absolute;
top: 30px;
left: 200px;
background-image: url(http://www.sanbarcomputing.com/images/class-header-css3.jpg);
background-size: 200px 200px;
width: 200px;
height: 200px;
transition: 1s;
}
JavaScript:
(function () {
var zeroEl = document.getElementById('zero'),
oneEl = document.getElementById('one'),
twoEl = document.getElementById('two'),
link1El = document.getElementById('link1'),
link2El = document.getElementById('link2');
function mouseover (elem) {
elem.style.opacity = '.2';
zeroEl.style.opacity = '.2';
}
function mouseout (elem) {
elem.style.opacity = '1';
zeroEl.style.opacity = '1';
}
document.addEventListener('DOMContentLoaded', function () {
link1El.addEventListener('mouseover', function () {
mouseover(oneEl); }, false);
link2El.addEventListener('mouseover', function () {
mouseover(twoEl); }, false);
link1El.addEventListener('mouseout', function() {
mouseout(oneEl); }, false);
link2El.addEventListener('mouseout', function () {
mouseout(twoEl); }, false);
}, false);
})();
jsfiddle
I could not get the CSS hover solution to work, for whatever reason.
NOTE: This solution uses modern JavaScript techniques that may not be compatible with legacy browsers
EDIT: Updated to use Pavlo's opacity solution, fixed css errors, changed image alignments, made images independent divs
First, assign a class to each link:
<div id="menu">
one</div>
two</div>
</div>
Then, if your css hover does not work, try using jQuery to do the hovering:
$('.link').hover(function() {
//handle mouse enter
}, function() {
//handle mouse leave
});
Refer: http://api.jquery.com/hover/

Categories

Resources