Stop fixed element scrolling at certain point? - javascript

I have a fixed position nav that fades in at a set scroll point.
I now need to stop it scrolling just before the page footer (about 400px from the bottom). I know the way to do this would be to change the position from fixed to absolute but I'm not sure how to implement that through jquery?
Live Example on the jsFiddle
jQuery:
var isVisible = false;
$(window).scroll(function(){
var shouldBeVisible = $(window).scrollTop()>1000;
if (shouldBeVisible && !isVisible) {
isVisible = true;
$('#floatingnav').fadeIn('slow');
} else if (isVisible && !shouldBeVisible) {
isVisible = false;
$('#floatingnav').fadeOut('fast');
}
});
CSS:
#floatingnav{
position: fixed;
top: 40px;
display: none;
}

Check if the bottom position of the navigation is below the footer top position. If this is the case, set a class or a specific css-prop.
$(window).scroll(function(){
var windowTopPos = $(window).scrollTop();
var footerTopPos = $('#footer').offset().top;
var navBottomPos = $('#floatingnav').offset().top + $('#floatingnav').outerHeight();
if(navBottomPos >= footerTopPos) {
$('#floatingnav').css('position', 'absolute');
}
});

Related

Adding a class between when user scrolls between 200px and 300px only

I'm trying to add a class to div when a user scrolls down the page. When they scroll 200px down the page I want a class to be added and then once the user scrolls down 300px I want the class to be removed. Similarly, when the user scrolls back up the page and is 300px from the top, I then want the class to be added until it gets to 200px and then removes the class.
I've tried so many variations but I think I'm approaching it all wrong. Here's a jsfiddle of how far I've gotten so far - http://jsfiddle.net/v8my3sr1/
$(window).on('scroll',function() {
var scroll = $(window).scrollTop();
if (scroll >= 200) {
$(".trigger").addClass("wow");
} else {
$(".trigger").removeClass("wow");
}
});
You condition is incorrect. You should check that scroll value is between 200 and 300
$(window).on('scroll',function() {
var scroll = $(window).scrollTop();
if (300 >= scroll && scroll >= 200) {
$(".trigger").addClass("wow");
} else {
$(".trigger").removeClass("wow");
}
});
$(window).on('scroll',function() {
var scroll = $(window).scrollTop();
if (300 >= scroll && scroll >= 200) {
$(".trigger").addClass("wow");
} else {
$(".trigger").removeClass("wow");
}
});
.container {
height: 2000px;
}
.trigger {
margin-top: 300px;
height: 400px;
}
.wow {
background: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="trigger">
<h1>Trigger container</h1>
</div>
</div>
Also you can use .toggleClass() to simplifying code
$(window).on('scroll',function() {
var scroll = $(window).scrollTop();
$(".trigger").toggleClass("wow", 300 >= scroll && scroll >= 200);
});
Try something like this :
$(window).scroll(function(){
var cutoff = $(window).scrollTop();
$(".trigger").each(function(){
if($(this).offset().top + $(this).height() > cutoff){
panel.removeClass('wow');
$(this).addClass('wow');
return false;
}
})
})

Bootstrap fix div element on top and prevent resize

I want to fix a div element on the top by scrolling.
I have achieved this with the following code:
Javascript:
$(function () {
var msie6 = $.browser == 'msie' && $.browser.version < 7;
if (!msie6) {
var top = $('#betslip').offset().top - parseFloat($('#betslip').css('margin-top').replace(/auto/, 0));
$(window).scroll(function (event) {
// what the y position of the scroll is
var y = $(this).scrollTop();
// whether that's below the form
if (y >= top) {
// if so, ad the fixed class
$('#betslip').addClass('fixed');
} else {
// otherwise remove it
$('#betslip').removeClass('fixed');
}
});
}
CSS:
#betslip.fixed {
position: fixed;
top: 0;
}
HTML:
<div class="col-md-12" id="betslip">
...
</div>
The problem is that, while scrolling the div element is getting larger. How I can fix/prevent this?
Here is a screenshot after and before the scrolling:
By adding position: fixed; to #betslip it will ignore width of container. Try adding width: inherit; to #betslip.fixed

How do I fix a div during scroll relative to Squarespace image blocks?

I'm trying to fade in/out and fix the blue div on the left when scrolled relative to the image blocks on the right.
http://www.warface.co.uk/#/testing/
pass: squared
.meta { /*This is the block I'm trying to stick/*
background: blue;
position: fixed;
width: 372px;
float: left;
z-index: 3;
right: 100%;
}
Here is the basics in JavaScript:
function controlMeta() {
var meta = document.querySelector("div.meta");
console.log(meta);
if (window.scrollY > 500) {
meta.style.display = "none";
} else {
meta.style.display = "block";
}
}
window.addEventListener("scroll", function () {
controlMeta();
})
You can get your elements scroll position with something like this:
document.getElementById("57513a9220c6475fb77061c5").getBoundingClientRect().top+window.scrollY
EDIT 1
Here is a method for associating elements with the meta box, based upon the previous:
//Load elements that affect the meta box
var meta = [];
var images = document.querySelector('.sqs-gallery').getElementsByTagName('img');
for (var i = 0; i < images.length; i++) {
meta.push({
node : images[i],
height : images[i].height,
//top is used with scroll position to determine which element we are looking at
top : images[i].getBoundingClientRect().top + window.scrollY
});
}
function controlMeta() {
meta.filter(function (el) {
//You might need to pad your filter a little
return window.scrollY < el.top && window.scrollY > el.top - el.height;
}).forEach(function (el) {
//These are the matching elements. I'm just fetching the src as an example
document.querySelector("div.meta div.body").innerHTML = el.node.src;
});
}
window.addEventListener("scroll", function () {
controlMeta();
});

Navigation bar fixed after scroll?

I have a navigation bar after header and i want that to be stuck at top of the page while scrolling down.
can i do with position:relative?? Unlike position:fixed with the help of the following script or any other better way?
$(window).scroll(function () {
if ($(window).scrollTop() > 100) {
$('#header').css('top', $(window).scrollTop());
}});
here is my fiddle!
black color background bar to be stuck at the top of the page
please help me out to fix that, thank you in advance.
Is this what you're trying to get?
window.onscroll = changePos;
function changePos() {
var header = document.getElementById("header");
if (window.pageYOffset > 70) {
header.style.position = "fixed";
header.style.top = "0";
} else {
header.style.position = "";
header.style.top = "";
}
}
Update: (I think, not sure) you can't scroll a fixed element, but you can an absolute one. So in the code below we're using position: absolute but making it behave like it's fixed. Now you can see the #header when you zoom in and scroll down.
window.onscroll = changePos;
function changePos() {
var header = document.getElementById("header");
if (window.pageYOffset > 70) {
header.style.position = "absolute";
header.style.top = pageYOffset + "px";
} else {
header.style.position = "";
header.style.top = "";
}
}
FIDDLE
you can solve it with css:
html:
<nav id= "header">
<ul>
<li> Link 1 </li>
<li> Link 2 </li>
<li> Link 3 </li>
</lu>
</nav>
css:
#header{
position: sticky;
top: 0; /* Position where u want it to stick, can be a
percentage, px, vh or any position unit */
z-index:150; /* to keep it on top layer in order not to be
masked by another element*/
}
check this link:
https://elextutorial.com/learn-css/css-position-fixed-scroll-sticky/

Animate/Ease an element to position when other elements disappear

Please take a look at this fiddle: http://jsfiddle.net/dhcyA/
Try clicking on a block. What I want is that when the other elements disapear, the selected block will animate/ease to his giving position instead of just jumping like it does now. Then the same animation repeats itself when clicking again on the box, but then back to place.
Maybe to keep in mind:
I'm using a reponsive design, which means those blocks can be vertical and horizontal after scaling the window.
Any redevisions on the fiddle or suggustions would be great!
Here is my solution.
On your existing markup, I added a wrapper division to calculate the position of boxes inside the wrapper. Like this
<div id="wrapper">
<div class="block">
<h2>I'm block 1</h2>
</div>
....
</div>
To maintain the fluidness of the block, I created a function to position the block on the wrapper. Here is the function for position of the blocks:
var reposition = function() {
wrapper = $("#wrapper");
console.log(wrapper.innerWidth());
pLeft = 0;
pTop = 0;
maxRowHeight = 0;
$(".block").each(function(){
if($(this).data('active')) {
$(this).data('top', pTop);
$(this).data('left', pLeft);
} else {
$(this).stop(0,0).animate({
'top' : pTop + 'px',
'left' : pLeft + 'px'
});
}
pLeft += $(this).outerWidth() + parseInt($(this).css('marginLeft'));
if($(this).height() > maxRowHeight) maxRowHeight = $(this).outerHeight() + parseInt($(this).css('marginTop')); //Find out the longest block on the row
if(pLeft + $(this).next().outerWidth() + parseInt($(this).next().css('marginLeft')) >= wrapper.innerWidth()) {
pLeft = 0;
pTop += maxRowHeight;
maxRowHeight = 0;
}
});
};
Finally, the script to toggle the block
$(".block").click(function() {
$(this).siblings().slideToggle('slow'); //Toggle other blocks
if(!$(this).data('active')){ //if the block is not active
$(this).data('left', $(this).position().left); //sets its left
$(this).data('top', $(this).position().top); // and top position
$(this).animate({ //animate at the top and bottom
top:0,
left:0
},'slow');
$(this).data('active',true);
}else{
$(this).animate({ //animate to its last known position
top:$(this).data('top'),
left:$(this).data('left')
},'slow');
$(this).data('active',false);
}
});
Demos
Demo[Full] (Resize this to see the fluidness maintained)
Demo[Full] (version showing variable heights)
Here is what this solutions gives:
Remembers the last position and gradually animate to/from this position
Block positions are calculated and animated on load and every resize
Repositioning happens on $(window).resize() thus maintaining the fluid nature of the block, despite the use of position absolute
Support variable heights
Minor change on existing markup & CSS
Also fixed two issues extended by Gaby
Accounts for each block margin independently
Recalculates the position of the element after resize
Final Update
Here is a full working solution (pretty straight forward in my opinion) with JS to set the positioning (a simple calculation) and CSS transitions for the rest..
Demo at http://jsfiddle.net/gaby/pYdKB/3/
It maintains the fluidity of float:left and works with any number of elements, and you can keep the :nth-child for the styling, and it will also work if you want to leave more than one element visible..
javascript
var wrapper = $('.wrapper'),
boxes = wrapper.children(),
boxWidth = boxes.first().outerWidth(true),
boxHeight = boxes.first().outerHeight(true);
function rePosition(){
var w = wrapper.width(),
breakat = Math.floor( w / boxWidth ); // calculate fluid layout, just like float:left
boxes
.filter(':not(.go)')
.each(function(i){
var matrixX = ((i)%breakat)+1,
matrixY = Math.ceil((i+1)/breakat);
$(this).css({
left:(matrixX-1) * boxWidth ,
top: (matrixY-1) * boxHeight
});
});
}
$('.box').click(function(){
$(this)
.siblings()
.toggleClass('go');// just add the go class, and let CSS handle the rest
rePosition(); // recalculate final positions and let CSS animate the boxes
});
$(window).resize(rePosition);
$(window).trigger('resize');
CSS
.wrapper{
position:relative;
}
.box{
width:200px;
height:100px;
position:absolute;
margin:5px;
cursor:pointer;
overflow:hidden;
text-align: center;
line-height: 100px;
-moz-transition-property: top,left,width,height;
-webkit-transition-property: top,left,width,height;
-ms-transition-property: top,left,width,height;
-o-transition-property: top,left,width,height;
transition-property: top,left,width,height;
-moz-transition-duration: 1s;
-webkit-transition-duration: 1s;
-ms-transition-duration: 1s;
-o-transition-duration: 1s;
transition-duration: 1s;
}
.go{
height:0;
width:0;
}
note: As #Athari correctly mentioned in the comments, you should include all browser prefixes for the widest support. (my initial answer only included moz / webkit and the standard)
Original Answer
You can not do it directly with your current HTML structure. The floated concept does not support it.
But if you can afford an extra wrapper, then it is no problem..
Just slide the contents of your extra wrapper element..
Put the float code on the wrapper element and use
$(document).ready(function() {
$(".block-wrapper").click(function() {
$(this).siblings().find('.block').slideToggle("slow");
});
});
Demo at http://jsfiddle.net/gaby/t8GNP/
Update #1
If you need to move the clicked element to the top left and back, then you cannot really do it with CSS.
You will need to manually position them (through JS), set CSS transitions (or jquery), and apply the new positions once you click.
Later on you might want more than one to remain visible and reposition as well..
So you might want to take a look at the great Isotope plugin which can handle this and a multitude of more situations/layouts
Here is my version:
http://jsfiddle.net/selbh/dhcyA/92/
(only javascript is changed, and it's responsive)
$(document).ready(function() {
$(".block").click(function() {
var $this = $(this);
var pos = $this.offset();
var $siblings = $(this).siblings().add(this);
var marginTop = $this.css('marginTop').replace(/[^-\d\.]/g, '');
var marginLeft = $this.css('marginLeft').replace(/[^-\d\.]/g, '');
var $clone = $this.clone();
$siblings.slideToggle("slow");
$clone.css({
position: 'absolute',
left: pos.left - marginLeft,
top: pos.top - marginTop,
'background-color': $this.css('background-color')
});
$('body').append($clone);
$this.css('opacity', 0);
$clone.animate({
'left': 0,
'top': 0
});
$clone.click(function() {
$siblings.slideToggle("slow", function() {
$clone.remove();
$this.css('opacity', 1);
});
$clone.animate({
left: pos.left - marginLeft,
top: pos.top - marginTop
});
});
});
});​
I'm kind of sleepy(It's 2:30 AM here) so I leave the half done answer here to give you an idea (I did it in 30 minutes so I guess with 30 minutes more you can get something really nice)
http://jsfiddle.net/LuL2s/2/
The trick comes by the block-holder which make the ease animation and making a difference between when they appear and disappear
JS
$(document).ready(function() {
var open = true;
$(".block").click(function() {
var $this = $(this);
var count = 0;
if (open) {
$this.parent().siblings().children().slideToggle("slow", function(){
if (count++ == 2) {
$this.parent().siblings().animate({width: 'toggle', height:'toggle'});
}
});
} else {
$this.parent().siblings().animate({width: 'toggle', height:'toggle'}, function(){
if (count++ == 2) {
$this.parent().siblings().children().slideToggle("slow");
}
});
}
open = !open;
});
});
HTML
<div class="block-holder">
<div class="block">
<h2>I'm block 1</h2>
</div>
</div>
<div class="block-holder">
<div class="block">
<h2>I'm block 2</h2>
</div>
</div>
<div class="block-holder">
<div class="block">
<h2>I'm block 3</h2>
</div>
</div>
<div class="block-holder">
<div class="block">
<h2>I'm block 4</h2>
</div>
</div>
CSS
.block {
width: 100%;
height: 100%;
text-align: center;
line-height: 100px;
cursor: pointer;
}
.block-holder:nth-child(1) .block {
background: green;
}
.block-holder:nth-child(2) .block {
background: red;
}
.block-holder:nth-child(3) .block {
background: orange;
}
.block-holder:nth-child(4) .block {
background: pink;
}
.block-holder {
width: 200px;
height: 100px;
float: left;
margin: 20px;
}
Great Challenge!
New Version:
Here is a much better version as it makes the blocks stay in their rows. I added a css function so that your nth-child styles could be applied even in the rows. Even maintains same HTML Structure.
Demo: http://jsfiddle.net/MadLittleMods/fDDZB/23/
The jQuery for this new revision looks like:
$('.block').on('click', function() {
var block = $(this);
// Keep the blocks in line
makeRows($('body'));
$('.block').not(this).each(function() {
// If sibling on the same level, horizontal toggle
// We also want ignore the toggleMethod if it is shown because we might need to reassign
if (($(this).position().top == block.position().top && (($(this).data('toggle') == -1) || $(this).data('toggle') == null)) || ($(this).data('toggle') != -1 && $(this).data('toggleMethod') == 'side'))
{
$(this).data('toggleMethod', 'side');
// Hide block
if ($(this).data('toggle') == -1 || $(this).data('toggle') == null)
{
// Set properties for later use in show block
$(this).data('overflowBefore', $(this).css('overflow'));
$(this).css('overflow', 'hidden');
$(this).data('marginBefore', $(this).css('margin'));
var width = $(this).width();
$(this).animate({
width: 0,
margin: 0
}, function() {
$(this).data('toggle', width);
});
}
// Show block
else
{
$(this).css('overflow', $(this).data('overflowBefore'));
$(this).animate({
width: $(this).data('toggle'),
margin: $(this).data('marginBefore')
}, function() {
$(this).data('toggle', -1);
});
}
}
// Do a normal vertical toggle
else
{
$(this).data('toggleMethod', 'top');
$(this).slideToggle('slow');
}
});
});
// Make rows to make the blocks in line
function makeRows(container)
{
// Make rows so that the elements stay where they should
var containerWidth = container.width();
var currentRowWidth = 0;
// Add styles first so nothing gets messed up
container.children().each(function() {
var itemCSS = css($(this));
$(this).css(itemCSS);
});
// Now assemble the rows
container.children().each(function() {
var blockWidth = $(this).outerWidth() + parseInt($(this).css('margin-left')) + parseInt($(this).css('margin-right'));
if((currentRowWidth + blockWidth) < containerWidth)
{
currentRowWidth += blockWidth;
}
else
{
Array.prototype.reverse.call($(this).prevUntil('.row')).wrapAll('<div class="row"></div>');
$(this).prev().append('<div class="row_clear" style="clear: both;"></div>');
currentRowWidth = 0;
}
});
}
// Remove the rows added
function deleteRows()
{
var content = $('.row').contents()
$('.row').replaceWith(content);
$('.row_clear').remove();
}
$(window).resize(function() {
deleteRows();
});
// Functions courtesy of marknadal
// https://stackoverflow.com/a/5830517/796832
function css(a)
{
var sheets = document.styleSheets, o = {};
for(var i in sheets) {
var rules = sheets[i].rules || sheets[i].cssRules;
for(var r in rules) {
if(a.is(rules[r].selectorText)) {
o = $.extend(o, css2json(rules[r].style), css2json(a.attr('style')));
}
}
}
return o;
}
function css2json(css)
{
var s = {};
if(!css) return s;
if(css instanceof CSSStyleDeclaration) {
for(var i in css) {
if((css[i]).toLowerCase) {
s[(css[i]).toLowerCase()] = (css[css[i]]);
}
}
} else if(typeof css == "string") {
css = css.split("; ");
for (var i in css) {
var l = css[i].split(": ");
s[l[0].toLowerCase()] = (l[1]);
};
}
return s;
}
I added a makeRows and deleteRows functions so that the blocks would stay in their rows instead of getting smaller and moving into the row above. I call deleteRows whenever the window resizes so that it can maintain a responsive layout. Then if the blocks need to be toggled, I recreate the rows.
css and css2json functions are courtesy of marknadal
Old version:
I came up with a solution with .animate so that it could ease horizontally.
Here is a demo: http://jsfiddle.net/MadLittleMods/fDDZB/8/
The jQuery looks like:
$('.block').on('click', function() {
var block = $(this);
$(this).siblings().each(function() {
// If sibling on the same level, horizontal toggle
// We also want ignore the toggleMethod if it is shown because we might need to reassign
if (($(this).position().top == block.position().top && ($(this).data('toggle') == -1) || $(this).data('toggle') == null) || ($(this).data('toggle') != -1 && $(this).data('toggleMethod') == 'side'))
{
$(this).data('toggleMethod', 'side');
// Hide block
if ($(this).data('toggle') == -1 || $(this).data('toggle') == null)
{
// Set properties for later use in show block
$(this).data('overflowBefore', $(this).css('overflow'));
$(this).css('overflow', 'hidden');
$(this).data('marginBefore', $(this).css('margin'));
var width = $(this).width();
$(this).animate({
width: 0,
margin: 0
}, function() {
$(this).data('toggle', width);
});
}
// Show block
else
{
$(this).css('overflow', $(this).data('overflowBefore'));
$(this).animate({
width: $(this).data('toggle'),
margin: $(this).data('marginBefore')
}, function() {
$(this).data('toggle', -1);
});
}
}
// Do a normal vertical toggle
else
{
$(this).data('toggleMethod', 'top');
$(this).slideToggle('slow');
}
});
});​
The key was to separate the blocks that were toggled with .slideToggle and .animate because you have to apply the same when they show and hide.

Categories

Resources