I have 2 divs:
<div id="div1"></div>
<div id="div2"></div>
I would like to add css({'position':'relative'}) to div2 ONLY when a user scrolls the page and div1 is no longer visible on the page. When div1 is visible again, I want to remove the css rule.
Can anyone help me?
You could try this code:
$(window).bind('scroll', function() {
if ($(document).scrollTop() >= $('#div1').offset().top - $(window).height()) {
//Change css relative
}else{
}
});
You should play with that until you got a perfect result. Good luck
this is not perfect, but i've written you a quick plugin to take care of this:
http://jsfiddle.net/m7ztR/1/
it can be used in two ways:
it gives you a new selector
console.log( $( "#elem" ).is( ":inView" )? "visible":"invisible" );
you can also use this to get all visible elements, e.g.
console.log( $( "p:inView" ).get() );
and something like an event-listener
$( "#watchThisElement" ).visibilityChange( function( visible ){
if( visible ){
// do this ...
}
else{
// do that ...
}
} );
note: this is not complete for a bunch of reasons!!
it only handles vertical scrolling
it does not handle iframes (and might screw up horribly with nested scrolling)
there is no unbind
it might get slow if you watch a lot of elements
does not take window resizing into account
it's something i quickly hacked up from similar code i had sitting around.
if the jsfiddle link ever dies, here's the actual code and markup:
html:
<div id="status">look at my background color!</div>
<div id="above">lots of space above ... </div>
<div id="trackMe">i'm being tracked!</div>
<div id="below">lots of space below ... </div>
css:
#status{
position: fixed;
right: 0;
top: 0;
background-color: red;
}
#above, #below{
height: 800px;
background-color: yellow;
}
javascript:
/**
* jquery (in)visibility plugin ...
*/
(function( $ ){
var w = $( window );
// add a custom ":inView" selector
$.expr[':'].inView = function(obj){
var $this = $(obj);
var relY = $this.offset().top - w.scrollTop();
return relY >= 0 && relY <= w.height();
};
$.fn.visibilityChange = function( fun ) {
return this.each(function() {
var elem = $(this);
var pVisible = elem.is( ":inView" );
$( document ).scroll( function( e ){
if( pVisible != elem.is( ":inView" ) ){
pVisible = !pVisible;
fun( pVisible );
}
});
});
};
})( jQuery );
$( "#trackMe" ).visibilityChange( function( visible ){
$( "#status" ).css( "background-color", visible? "green":"red" );
} );
I think you should do some tricks with window.onscroll, the window height and the heigt of div1. As in...do some code in the onscroll method...check for the window height, check for the height of div1, do some calculations on it and check if the div is out of sight...if true...remove the css.
Related
Two questions:
Focus on the part of 'Get early access' bar. It is positioned with position:relative and I want to have it sticky once you move to the 2nd section. I've tried to add helper with the same height in order to get smooth transition when I change the .class to fixed. But not working.
This with helper in previous websites helped me but now it doesn't work and it really bothers me.
What would be alternative to position sticky which works in all browsers? In this particular case, how needs jquery to look like?
Thanks in advance.
/**
* Zirelco
* Custom JS functions
*/
jQuery(document).ready(function ( $ ) {
var mn = $("#sticky-wrapper");
mns = "nav--scrolled";
hdr = $("#top-wrapper-v1").height();
$(window).scroll(function() {
if( $(this).scrollTop() > hdr ) {
mn.addClass(mns);
} else {
mn.removeClass(mns);
}
});
$('.cookies .btn').on('click', function() {
if ($('.cookies').css('opacity') == 0) {
$('.cookies').css('opacity', 1);
}
else {
$('.cookies').addClass('none');
}
});
});
Edit V3
Try this Code instead of yours:
(function(selector) {
selector = selector || '#sticky-wrapper';
var stickyWrapper = document.querySelector(selector)
var stickyTrigger = document.createElement('div')
stickyTrigger.classList.add('sticky-trigger')
stickyWrapper.parentElement.insertBefore(stickyTrigger, stickyWrapper)
var listener = function (e) {
if (stickyTrigger.getBoundingClientRect().top < 0) {
stickyWrapper.classList.add('sticky');
} else {
stickyWrapper.classList.remove('sticky');
}
}
var onScroll = document.addEventListener('scroll', listener);
}('#sticky-wrapper'))
What this does is:
create a .sticky-trigger element
insert this right before #sticky-wrapper
watch for scroll event of document
check the top property of getBoundingClientRect of the .sticky-trigger element
toggle the sticky class of #sticky-wrapper depending on the sign (positive or negative) of that top value
You don't have to change your HTML output at all
Old V1
You use the height of the #top-wrapper-v1 <section> as trigger for the class toggle. But you totally forget the to calc the <header> height as well.
To prevent such mistakes just go for the top edge of the '#sticky-wrapper' as a trigger
// $(window).scroll(function(e) {
// if( $(this).scrollTop() > mn.offset().top ) {
// mn.addClass('sticky');
// } else {
// mn.removeClass('sticky');
// }
//});
Old V2
Because of the comment of the asker, this is an improved way of doing it.
In the previous example, the measurement of the offset().top of #sticky-wrapper is immediately set to 0 caused by position: fixed. In order to break this issue, we wrap the #sticky-wrapper in a trigger element, measure the offset().top of that element as trigger. This trigger element will remain in the document flow and will not be fixed
HTML
<!--
<section id="sticky-trigger">
<section id="sticky-wrapper" class="">
<div class="container" style="position: fixed;top: 0;">
Other content
</div>
</section>
</section>
-->
JavaScript
// var trigger = document.querySelector('#sticky-trigger')
// $(window).scroll(function(e) {
//
// if( $(this).scrollTop() > trigger.offset().top ) {
// mn.addClass('sticky');
// } else {
// mn.removeClass('sticky');
// }
// });
I have a function(s) that expand a drop down list when the browser is size n.
Size n is determined by css media queries and works as expected.
However when I physically drag the width of the browser window out, the .click function remains bound to the element even though a media query indicates that a css hover effect takes place when the browser is size n + 1.
A refresh at any point produces the desired behavior but of course I don't want to require a refresh.
var section_4 = "#main-menu > li:nth-child(4) > a";
var sub_section_4 = "#main-menu > li:nth-child(4) > ul";
$( section_4 ).click(function() {
$( sub_section_4 ).slideToggle(300);
return false;
});
how can I apply this function only on a given size and not have the .click follow to the inline list ? thanks
You could use jquery .width() and .resize() like this:
var section_4 = "#main-menu > li:nth-child(4) > a";
var sub_section_4 = "#main-menu > li:nth-child(4) > ul";
var docWidth = (window).width();
$(document).resize(function(){
docWidth = (window).width(); // Refresh value on resize
});
$( section_4 ).click(function() {
if( docWidth < [integer] ){ // set integrer the same as your #media.
$( sub_section_4 ).slideToggle(300);
return false;
}
});
I want fadeIn and fadeOut effect on div one by one sequentially with regular interval. I tried with following code but it will all div at a time fadeIn and fadeOut.
HTML
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
jQuery
function fade()
{
$("div").each(function(){
$(this).fadeOut(3000);
});
$("div").each(function(){
$(this).fadeIn(3000);
});
}
setInterval(fade,6000);
Update
I want First of all div one by one disappear from screen. When all div disappear then one by one show. This should happen regular interval.
JS Fiddle
There are different ways to execute deferred actions sequentially. Here is a good article about this http://www.paulirish.com/2008/sequentially-chain-your-callbacks-in-jquery-two-ways/
According to the comments and your later defined needs, you can solve it like this and use fadeTo instead of fadeIn/fadeOut
var start = $('div:first');
function fade(lobj){
lobj.fadeTo('slow',lobj.css('opacity')==1 ? 0 : 1,function(){
var nobj = lobj.next();
if(nobj.length)
fade(nobj);
else
fade(start);
});
}
fade(start);
http://jsfiddle.net/sofl/1n30hp49/15/
I guess you should use the function next on jQuery, like this, I just wrote forking in your snippet on jsfiddle:
http://jsfiddle.net/1n30hp49/17/
In this code above I also used the $el to define who is going to be the next element:
function fade( el, timer ){
el.fadeOut( timer , function() {
$( this ).fadeIn( timer , function() {
fade( $( this ).next(), timer ) ;
} ) ;
} ) ;
}
var timer = 1000 ;
//run function
fade( $( "div:first-child" ), 1000 ) ;
try to do this, if so, let me know if it works! att.
A reliable way to do this would be to use jQuery animation queues and a deferred object in a recursive loop. The advantage of this method is the ease you can add animations into the fade function. It is also much easier to read than a bunch of nested callbacks.
// Get all the div elements
var divs = $('div');
/**
* Fade in a specific indexed div element
* #param {integer} i
* #return {object} $.promise
*/
function fade(i)
{
return divs.eq(i)
.fadeIn(3000)
.delay(6000)
.fadeOut(3000)
.promise();
}
/**
* Recursive sequence runner
* #param {integer} i
*/
function runSequence(i) {
// If i is null/false set it to 0
i = ! i ? 0 : i;
// Run animation on item i
var promise = fade(i);
// Use the promise to queue up the next item
// by calling this function again when the
// animation is complete
promise.then(function() {
if (i > divs.length) {
i = 0;
}
runSequence(++i);
});
}
// Run the sequence for the first time
runSequence();
JSFiddle example
There's been some great answers here but just to add to the diversity, here's another way with just good-ol' Fade in's and outs:
PLEASE NOTE
Stackoverflow's method of including third party scripts in their snippets interferes with this example so I had to add a container div around the other divs. For a purer example see the fiddle.
function fade() {
var thisObj = this;
thisObj.out = function(el, timer) {
el.fadeOut( timer , function() {
if ($( this ).prev().length > 0) {
thisObj.out( $( this ).prev(), timer );
} else {
thisObj.in( $( "#container").find("div:first-child" ), timer );
}
} ) ;
}
thisObj.in = function(el, timer) {
el.fadeIn( timer , function() {
if ($( this ).next().length > 0) {
thisObj.in( $( this ).next(), timer ) ;
} else {
thisObj.out( $( "#container").find("div:last-child" ), timer );
}
} );
}
}
new fade().out( $( "#container").find("div:last-child" ), 1000 );
#container div {
background : #00f;
height: 50px;
width : 50px;
margin : 5px;
float: left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
To change the order of the fade in and out, just change last-child to first-child.
I've read several suggestions on how to do this, but I can't find one that covers how to change the opacity once it reaches an offset on the page AND animate the change. Also keep in mind that I don't want to completely fade out the div, just cut the opacity by half once the user scrolls past a certain point.
I came up with this, but I can't figure out how to properly animate the change in opacity:
<script type="text/javascript">
var fadeStart=150
,fading = $('#header')
;
$(window).bind('scroll', function(){
var offset = $(document).scrollTop()
;
if( offset<=fadeStart ){
opacity=1;
}else if( offset>=fadeStart ){
opacity=0.5;
}
fading.css('opacity',opacity);
});
</script>
Instead of setting the final value at one using fading.css('opacity',opacity);, animate it by using fading.animate({opacity: opacity}, 'fast');
I think you can edit with this:
var fadeStart=150
,fading = $('#header')
;
$(window).bind('scroll', function(){
var offset = $(document).scrollTop()
,opacity=0
;
if( offset<=fadeStart ){
opacity=1;
}else if( offset<=fadeUntil ){
opacity=1-offset/fadeUntil;
}
fading.css('opacity',opacity)//.html(opacity)
;
});
I am new at JQuery and I have a specific question about the IF-THEN-ELSE fork.
The big problem for me is the syntax of this (I suck at Javascript). It would help me if anyone can "translate" the pseudo code into a JQuery (or Javascript) valide code.
The pseudo code:
IF "#Contentshowroom" css "left" is NOT > 1960px
THEN
On Click "#Forwardbutton" DO
animate "#Contentshowroom" css "left" =+980px
ELSE You can not click on the "#Forwardbutton"
Place the if() statement in the click handler for #Forwardbutton to test the left position of #Contentshowroom.
If you're using jQuery:
$('#Forwardbutton').click(function() {
var $Content = $('#Contentshowroom');
if( $Content.offset().left <= 1960 ) {
$Content.animate({ left: '+= 980' });
}
});
So now when you click the Forwardbutton, it will check the left .offset() position of the Contentshowroom to see if it is less than or equal to 1960px. And if so, it will animate the left position an additional 980px.
jQuery's .offset() method gives you the top/left positions relative to the body. If you want it relative to its parent container, then use jQuery's .position() method.
click doc
animate doc
offset doc
$("#Forwardbutton").click( function( e ){
// lookup is safe, no noticable performance cost.
// though a reference makes it more losely coupled.
// I'll leave it at your discretion.
var target = $("#Contentshowroom")
// NOTE: the offset parent should have position relative or absolute.
, leftPos = target.offset().left;
if ( leftPos < 1960 ) {
target.animate({
left : leftPos + 980
}); // see docs to tweak animation
} // else do nothing.
} );
Could also use e.preventDefault(); , but don't if it's not needed, it will safe you headaches if you add more listeners to your buttons and find out they're not working.
// first store contentShowroom and it's left property to save getting > 1
var contentShowroom = $('#Contentshowroom');
var showroomLeft = contentShowroom.css('left');
var forwardButton = $('#Forwardbutton');
if (showroomLeft <= 1960){
forwardButton.click(function(){
contentShowroom.animate({left: showroomLeft + 980);
}
}
else {
forwardButton.unbind('click');
}
if this is to be run once at the beginning then
if ( $('#Contentshowroom').offset().left > 1960 )
{
$('#Forwardbutton').click( function(){
$('#Contentshowroom').animate({left:'+=980'});
} );
}
else
{
// if the #Contentshowroom is a link then
$('#Contentshowroom').removeAttr('href');
// if the #Contentshowroom is a button then
// $('#Contentshowroom').attr('disabled',true);
}