How to remove scroll event after functions have finished - javascript

I am attaching a scroll event listener to a wrapper div, and then for each div with the class of "js-chart" it will check to see if that particular js-chart is within view and will execute a function on it. It will then remove the js-chart class from that element so that it won't keep on firing the function.
This all works fine, but what I'd like to do is that when all of the js-charts have been triggered (and thus there are no more elements with js-chart, to completely remove the scroll event listener so I'm not firing off pointless checks all the time.
$('#wrapper').scroll(function() {
var $wrapper = $(this);
$('.js-chart').each( function (n) {
var $this = $(this);
if ( $this.position().top + $this.height() > 100 && $this.position().top < $wrapper.height() ) {
console.log("Test");
createChart($this);
$this.removeClass('js-chart');
}
});
});

Add something like this:
$('#wrapper').on("scroll", function() {
var $wrapper = $(this);
$('.js-chart').each( function (n) {
var $this = $(this);
if ( $this.position().top + $this.height() > 100 && $this.position().top < $wrapper.height() ) {
console.log("Test");
createChart($this);
$this.removeClass('js-chart');
}
});
if (!$('.js-chart').length) {
$wrapper.off("scroll");
}
});

I think you can do so:
$('#wrapper').on("scroll", function() {
var $wrapper = $(this);
$('.js-chart').each( function (n) {
var $this = $(this);
if ( $this.position().top + $this.height() > 100 && $this.position().top < $wrapper.height() ) {
console.log("Test");
createChart($this);
$this.removeClass('js-chart');
}
});
if (!$(".js-chart")[0]) $wrapper.off("scroll");
});

Related

Showing multiple sticky header while scroll down the webpage

I am a beginner in jQuery. As willing to develop a mobile website, I would like to implement sticky header for enhancing the user experiences.
Here are some codes of my work but it seems not work as calculation of the "top".
HTML:
<div class="fixed-header">
<div class="header_item" id="head_a"> A </div>
<div class="header_item" id="head_b"> B </div>
<div class="header_item" id="head_c"> C </div>
<div class="header_item" id="head_d"> D </div>
<div class="header_item" id="head_e"> E </div>
</div>
javascript and jQuery:
var $totalImageHeight;
$(window).on("load", function() { //Fires when DOM is loaded
getImageSizes();
$(window).resize(function() { //Fires when window is resized
getImageSizes();
});
});
function getImageSizes() {
$(".insurance_item img").each(function(count) {
var $this = $(this);
$imageHeight = $this.height();
$totalImageHeight = $imageHeight * (count + 1);
});
}
var stickyHeaders = (function() {
var $window = $(window),
$stickies;
var load = function(stickies) {
if (typeof stickies === "object" && stickies instanceof jQuery && stickies.length > 0) {
$stickies = stickies.each(function() {
var $thisSticky = $(this).wrap('<div class="followWrap" style="height: 0;"/>');
$thisSticky
.data('originalPosition', $thisSticky.offset().top)
.data('originalHeight', $thisSticky.outerHeight( 75 ))
.parent().height($thisSticky.outerHeight( 75 ));
});
$window.off("scroll.stickies").on("scroll.stickies", function() {
_whenScrolling();
});
}
};
var _whenScrolling = function() {
$stickies.each(function(i) {
var $thisSticky = $(this),
$stickyPosition = $thisSticky.data('originalPosition');
if ($stickyPosition <= $window.scrollTop()) {
var $nextSticky = $stickies.eq(i + 1);
$nextStickyPosition = $totalImageHeight - $nextSticky.data('originalPosition') - $thisSticky.data('originalHeight');
$thisSticky.addClass("fixed").css("top", $nextStickyPosition);
if ($nextSticky.length > 0 && $thisSticky.offset().top >= $nextStickyPosition) {
$thisSticky.addClass("absolute").css("top", $nextStickyPosition);
}
}else{ //scroll up and disable the fixed header
var $prevSticky = $stickies.eq(i - 1);
$thisSticky.removeClass("fixed");
if($prevSticky.length>0&&$window.scrollTop()<=
$thisSticky.data('originalPosition') -
$thisSticky.data('originalHeight')){
$prevSticky.removeClass("absolute").removeAttr("style");
}
}
});
};
return {
load: load
};
})();
$(function() {
stickyHeaders.load($(".header_item"));
});
Sorry for the late answer. I create an new javascript for implementing this function.
Code:
var $window = $(window);
var imageArr = [];
var iOS = !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform);
console.log(iOS);
$(window).on("load", function() { //Fires when DOM is loaded
getImageSizes();
window.requestAnimationFrame(tick);
$(window).resize(function() { //Fires when window is resized
getImageSizes();
scrollDetect(imageArr);
});
});
function getImageSizes() { //get the numbers of images and its outerHeight
var $items = $(".insurance_item");
for(var c = 0; c < $items.length; c++){
imageArr[c] = $($items[c]).outerHeight();
}
}
function scrollDetect(imageArr){ //show the title header of image once the image is offscreen.
var $items = $('.header_item');
for(var c = 0; c < imageArr.length; c++){
if($window.scrollTop() >= (imageArr[c] * (c+1)) - $('#fixed-header').outerHeight()){
$items.eq(c).show();
}else{
$items.eq(c).hide();
}
}
window.requestAnimationFrame(tick); //prevent the frame lost and make a incorrect calculation
}
function tick(){
scrollDetect(imageArr);
};
May be some better solution for solving this situation, thanks for everyone.

jQuery change active class from a to li

We use some jQuery to make our navigation sticky from some point on the page and to add an active class to sections on the page. Currently the active class is added to the a class inside the li. But I want to add this to the li instead of the a href.
How can I achieve this?
Code:
<script>
$j(window).scroll(function(){
var sticky = $j('.menu-header-product'),
scroll = $j(window).scrollTop();
var elementOffset=jQuery("#productnav").offset();
if (scroll >= elementOffset.top - 57) sticky.addClass('sticky');
else sticky.removeClass('sticky');
});
$j(window).scroll(function(){
var sticky = $j('.content'),
scroll = $j(window).scrollTop();
var elementOffset=jQuery("#productnav").offset();
if (scroll >= elementOffset.top - 57) sticky.addClass('sticky');
else sticky.removeClass('sticky');
});
$j(document).ready(function () {
$j(document).on("scroll", onScroll);
$j('a[href^="#"]').on('click', function (e) {
e.preventDefault();
$j(document).off("scroll");
$j('li').each(function () {
$j(this).parent().removeClass('active');
})
$j(this).parent().addClass('active');
var target = this.hash;
$jtarget = $j(target);
$j('html, body').stop().animate({
'scrollTop': $jtarget.offset().top - 120 /**just subtract the height of the fixed html part */
}, 500, 'swing', function () {
window.location.hash = target;
$j(document).on("scroll", onScroll);
});
});
});
function onScroll(event){
var scrollPosition = $j(document).scrollTop();
$j('nav li').each(function () {
var currentLink = $j(this);
var refElement = $j(currentLink.attr("href"));
if (refElement.position().top - 125 <= scrollPosition && refElement.position().top - 125 + refElement.height() > scrollPosition) {
$j('nav ul li a').parent().removeClass("active");
currentLink.parent().addClass("active");
}
else{
currentLink.parent().removeClass("active");
}
});
}
</script>
You can use jQuery's parent selector. So
$j(this).addClass('active');
becomes
$j(this).parent().addClass('active');
You would also need to change the selector $j('a') to $j('li'). I would recommend filtering to only lis in your navigation, i.e. $j('nav li'). Finally, you'd have to update the onScroll event similarly.

How to check current window location against an array of values

Let's say we've got this HTML:
<div class="locationhash" data-position="0"></div>
<div class="locationhash" data-position="1000"></div>
<div class="locationhash" data-position="3000"></div>
<div class="locationhash" data-position="5000"></div>
<div class="locationhash" data-position="8000"></div>
where data-positions value, in normal conditions would equal to elements height from pages top in px.
Im retrieving elements with .locationhash, reading its data-position and pushing both values as an object into an array using this code. (works as intended, output in third code sample here)
var getSections = function() {
var pageSectionsArr = [];
var pageSections = (document).querySelectorAll('.locationhash');
Array.prototype.forEach.call(pageSections, function(section) {
var getSectionPos = section.getAttribute('data-position');
var pageSectionObj = {
pageSection : section,
pageSectionPos : getSectionPos
};
pageSectionsArr.push(pageSectionObj);
});
console.log(pageSectionsArr)
};
(function recalcSectionPos() {
$(window).resize(function() {
getSections();
});
})();
Output looks like this:
[ {div.locationhash, value of its data-position},
{div.locationhash, value of its data-position}, ... ]
Now i need to check on scroll wether current window.scrollTop() equals any of data-position values so:
$(window).scroll(function() {
var hFromTop = $(window).scrollTop()
//cant figure out what should i do next (jquery allowed)
});
Question: How could i check current window.scrolltop() against multiple values, while user is scrolling so i could make something happen when window.scroll top equals to a value
(I'm using this array because later im gonna read .locationhash's id in order to append it to url using location replace)
DEMO
$(document).ready(function () {
$(document).on("scroll", onScroll);
//smoothscroll
$('a[href^="#"]').on('click', function (e) {
e.preventDefault();
$(document).off("scroll");
$('a').each(function () {
$(this).removeClass('active');
})
$(this).addClass('active');
var target = this.hash,
menu = target;
$target = $(target);
$('html, body').stop().animate({
'scrollTop': $target.offset().top+2
}, 500, 'swing', function () {
window.location.hash = target;
$(document).on("scroll", onScroll);
});
});
});
function onScroll(event){
var scrollPos = $(document).scrollTop();
$('#menu-center a').each(function () {
var currLink = $(this);
var refElement = $(currLink.attr("href"));
if (refElement.position().top <= scrollPos && refElement.position().top + refElement.height() > scrollPos) {
$('#menu-center ul li a').removeClass("active");
currLink.addClass("active");
}
else{
currLink.removeClass("active");
}
});
}
I followed #tushar gupta's advice, also wrapped it in a function checking wether scrolling ended
$(window).scroll(function() {
clearTimeout($.data(this, 'scrollTimer'));
$.data(this, 'scrollTimer', setTimeout(function() {
var hFromTop = $(window).scrollTop();
$.each(pageSectionsArr, function(obj) {
console.log(this + 'is sparta')
if (~~this.pageSectionPos <= hFromTop && ~~this.pageSectionPos + this.pageSection.offsetHeight > hFromTop) {
//console.log(this.pageSectionPos)
var sectionsId = this.pageSection.id
//console.log(sectionsId)
window.location.replace('#/'+sectionsId)
};
});
}, 250));
});

.next() not working as intended

So,
if($(this).hasClass('active')){
$(this).removeClass('active');
$(this).prev().addClass('active');
}
works fine, it adds the class "active" to this previous div of the same kind.
if($(this).hasClass('active')){
$(this).removeClass('active');
$(this).next().addClass('active');
}
However, adds the class to the next div (as i intend for it to do) for about 0.5 of a second BUT then removes it.
Here's ALL of the jQuery (as per your comments below) - Please do not comment on my horrible code organization
$(window).load(function () {
// Initial variables
var numberSlides = 0;
var currentSlide = 1;
var ready = true;
var pageWidthR = $(document).width() - 352;
var pageWidthL = $(document).width() - 352;
// Update number of slides by number of .slide elements
$('#features-slider .slide').each(function () {
numberSlides++;
});
// Go through each slide and move it to the left of the screen
var i = 0;
$($('#features-slider .slide').get().reverse()).each(function () {
if (i == 0) {
} else {
var newWidth = i * 115;
$(this).css('left', '-' + newWidth + '%');
}
i++;
});
// Animate the first slide in
$('#features-slider .slide:last-child').addClass('active').animate({
left: 0
}, 1500);
// Remove the loading message
$('#loading').fadeOut(1000, function () {
$('#loading').remove();
// Now that we're done - we can show it
$('#features-slider').show();
});
/***** Left and Right buttons *****/
/* Right */
$('#rightbutton').click(function () {
var numberSlides = 0;
$('#features-slider .slide').each(function () {
numberSlides++;
});
var index = $('.slide.active').index() + 1;
if (!$('.slide').is(':animated') && index != 1) {
$('#features-slider .slide').each(function () {
if ($(this).hasClass('active')) {
var currentLeft = $(this).css('left');
var newLeft = parseInt(currentLeft) + 115;
} else {
var currentLeft = $(this).css('left');
var newLeft = parseInt(currentLeft) + 115;
}
$(this).animate({
left: newLeft + '%'
}, 1500);
if ($(this).hasClass('active')) {
$(this).removeClass('active');
$(this).prev().addClass('active');
}
});
}
});
/* Left */
$('#leftbutton').click(function () {
var numberSlides = 0;
$('#features-slider .slide').each(function () {
numberSlides++;
});
var index = $('.slide.active').index() + 1;
if (!$('.slide').is(':animated') && index != numberSlides) {
$('#features-slider .slide').each(function () {
if ($(this).hasClass('active')) {
var currentLeft = $(this).css('left');
var newLeft = parseInt(currentLeft) - 115;
} else {
var currentLeft = $(this).css('left');
var newLeft = parseInt(currentLeft) - 115;
}
$(this).animate({
left: newLeft + '%'
}, 1500);
if ($(this).hasClass('active')) {
$(this).next().addClass('active');
$(this).removeClass('active').not($(this).next());
}
});
}
});
});
$(document).ready(function () {
// Hide the slider and show a loading message while we do stuff and the images / DOM loads - Also disable overflow on the body so no horizontal scrollbar is shown
$('body').css('overflow-x', 'hidden');
$('#features-slider').hide();
$('#loading').html('<center> <img id="loader" src="/wp-content/themes/responsive/library/images/ajax-loader.gif" /> Loading</center>');
});
RESOLVED
New left button function :
$('#leftbutton').click(function(){
var numberSlides = 0;
$('#features-slider .slide').each(function(){
numberSlides++;
});
var index = $('.slide.active').index()+1;
if( !$('.slide').is(':animated') && index != numberSlides ){
var done = false;
$('#features-slider .slide').each(function(){
if($(this).hasClass('active')){
var currentLeft = $(this).css('left');
var newLeft = parseInt(currentLeft)-115;
} else {
var currentLeft = $(this).css('left');
var newLeft = parseInt(currentLeft)-115;
}
$(this).animate({left: newLeft+'%'}, 1500);
if($(this).hasClass('active') && done == false){
$(this).next().addClass('active');
$(this).removeClass('active');
done = true;
}
});
});
If you're iterating forward through the elements, then it should be clear what's going on - you add the "active" class to the next element, and then the next iteration takes it away.
This is just a guess however as you did not post enough code for me (or anybody else) to be sure.
edit — ok now that you've updated the question, it's clear that the guess was correct. The .each() function will iterate forward through the elements. When an element has the "active" class, and the code removes it and adds it to the next element, then on the next iteration the work is undone.
Since you are referencing this and by the behavior you're describing, you are likely iterating a loop for a list of elements. As a result, you are completing the action you want but the next iteration is removing the previous changes due to your usage of removing a class and then adding the class back.
As it stands now, your code does not illustrate how this occurence can be happening.
Update:
As suspected, you seem to be looping as signified by: each(function(){. While iterating through your objects the class is being pushed forward and is not acting as desired. You are stating add the class to the next element, but remove it from the current element, and this behavior continues through your iteration.
On a side note, update your code to call removeClass() on the current object first, before adding it to the next object:
if ($(this).hasClass('active')) {
$(this).removeClass('active').next().addClass('active');
}

jQuery click event

In the following code, the click event is added explicitly to the body, so that even if click outside the button say on the body the div with ID tip1 should close with a fade effect.
The problem here is that the the div closes even if we click on the div itself.
Any idea on this would help ..
$(document).ready(function(){
$('.button').getit({speed: 150, delay: 300});
});
$.fn.getit = function(options){
var defaults = {
speed: 200,
delay: 300
};
var options = $.extend(defaults, options);
$(this).each(function(){
var $this = $(this);
var tip = $('.tip');
this.title = "";
var offset = $(this).offset();
var tLeft = offset.left;
var tTop = offset.top;
var tWidth = $this.width();
var tHeight = $this.height();
$this.click(
function() {
if($('.tip').hasClass('.active101')) {
$('.tip').fadeOut("slow");
$('.tip').removeClass('.active101').addClass('.inactive101');
}
else {
setTip(tTop, tLeft);
$('body').bind('click',function(e) {
var parentElement = "button1";
var parentElement2 = "tip1"
if( e.target.id != parentElement) {
$('.tip').fadeOut("slow");
$('.tip').removeClass('.active101').addClass('.inactive101');
}
});
$('.tip').removeClass('.inactive101').addClass('.active101');
setTimer();
}
},
function() {
if($('.tip').hasClass('.inactive101')) {
stopTimer();
tip.hide();
}
}
);
setTimer = function() {
$this.showTipTimer = setInterval("showTip()", defaults.delay);
}
stopTimer = function() {
clearInterval($this.showTipTimer);
}
setTip = function(top, left){
var topOffset = tip.height();
var xTip = (left-440)+"px";
var yTip = (top-topOffset+100)+"px";
tip.css({'top' : yTip, 'left' : xTip});
}
showTip = function(){
stopTimer();
tip.animate({"top": "+=20px", "opacity": "toggle"}, defaults.speed);
}
});
};
<div class="main">
<a href="#" class="button" id="button1">
Click Me!
</a>
<div class="tip" id="tip1">Hello again</div>
</div>
Set the click event on your div to 'stopPropagation'
http://api.jquery.com/event.stopPropagation/
Perhaps you can bind a click event to the div itself and prevent the click event from bubbling up?
$("#div").click(function(e) {
e.stopPropagation();
});
Rick.
you should stop the event's propagation
you should check if any of the element's children are clicked (if you click a child of that element, the element would close)
you should probably be more careful with your selectors(if you intend to use this only on one element - because you are verifying with an id ('#button1' which is unique), you shouldn't bind the getit function to all the elements with class '.button')
if( e.target.id != parentElement
&& $(e.target).parents('#'+parentElement).length == 0) {
$('.tip').fadeOut("slow");
$('.tip').removeClass('.active101').addClass('.inactive101');
e.stopPropagation();
}
thanks for your answers. Tried with all of them and each worked. But I also found another solution to this by just adding another condition:
if (e.target.id != parentElement && e.target.id != parentElement2) {
$('.tip').fadeOut("slow");
$('.tip').removeClass('.active101').addClass('.inactive101');
}

Categories