launch function once if class is added on visible Element [duplicate] - javascript

This question already has an answer here:
jQuery trigger when 2/3s of div are in viewport
(1 answer)
Closed 6 years ago.
I would like to check if a class is added on an Element during the scroll and launch another function, just once, until the class is added to the next element.
here my first function :
function onScreen() {
$('article').each(function() {
var thisTop = $(this).offset().top - $(window).scrollTop();
var middleScreen = $(window).height() / 2;
if (thisTop < middleScreen && (thisTop + $(this).height()) > middleScreen) {
$(this).addClass('visible');
} else {
$(this).removeClass('visible');
}
});
}
// launch
$(window).on('load scroll', onScreen);
My seconde function : I would like to launch just once if the visible class is added to another article.
function textChange() {
var v = $('article.visible');
var el = $('.el', v);
var text = $(el).html();
$('.titre').html(text);
}
$(window).on('load scroll', textChange);
note : It's important to me to keep two separate function.
My issue is the scroll function get call textChange() each pixel I scroll on the page. Strangly, the onScreen() function add the visible class just once until the next article gonna be visible.
Thanks for yours suggestions.

Introduce a global variable that tells you if the function has been executed or not:
var textChanged = false;
In your textChange() function set it to true when the function was called the first time:
function textChange() {
if(!textChanged){
textChanged = true;
var v = $('article.visible');
var el = $('.el', v);
var text = $(el).html();
$('.titre').html(text);
}
}
EDIT: Alternatively you can do the check before the function gets called:
if (thisTop < middleScreen && (thisTop + $(this).height()) > middleScreen) {
$(this).addClass('visible');
if(!textChanged){
textChange();
}
} else {
$(this).removeClass('visible');
}

Related

Scroll is not binded, and I am stuck

var id = document.querySelector("#arrowUp");
var arrowUp = window.pageYOffset >= 5;
console.log(arrowUp);
id.classList.toggle("arrowUp", arrowUp);
id.addEventListener('click', function() {
window.scrollTo(0, 0);
})
I am trying to insert a class in certain HTML based on the scroll, but the issue is this part of the code:
var arrowUp = window.pageYOffset >= 5;
It is not dynamically getting true or false based on the scroll and the condition window.pageYOffset >= 5; is not dynamically true or false right now. Hence anticipated class is not getting inserted/deleted.
How to bind this part of the code to scroll event
Add an EventListener for the scroll event.
var id = document.querySelector("#arrowUp");
id.addEventListener('click', function() {
window.scrollTo(0, 0);
})
window.addEventListener('scroll', function() {
var arrowUp = window.pageYOffset >= 5;
if (arrowUp == true) {
id.classList.add('visible');
} else {
id.classList.remove('visible');
}
})
You can't do this "dynamically", window.pageYOffset is not a magic dynamic variable, it's a getter (function without arguments that retrieves some value)
In order for this to work, you have to check on each scroll event is the condition holds true.
var id = document.querySelector("#arrowUp");
document.addEventListener("scroll", function() {
var arrowUp = window.pageYOffset >= 5;
console.log(arrowUp);
id.classList.toggle("arrowUp", arrowUp);
});
id.addEventListener('click', function() {
window.scrollTo(0, 0);
});

Get static value from variable (scroll function)

I have a "follow scroll" function, but I want it to turn off when it returns to a certain point. My code is as follows:
scrollSidebar: function(scroll) {
var elemPos = $('#bestvideos-2').offset().top,
scroll2 = scroll;
if(scroll2 >= elemPos) {
$('#bestvideos-2').animate({
'margin-top':(scroll - 315)+'px'
},0);
} else {
$('#bestvideos-2').css('margin-top','0');
}
}
$(window).scroll(function() {
var scrollHeight = $(window).scrollTop();
Scroll.scrollSidebar(scrollHeight);
})
The problem is - every time I get up, it goes way up, not following scroll. What I'm thinking is storing a variable elemPos somewhere and keep it static (now it's changing each time I scroll).
What can I do with this?
Pass the value to the scrollSidebar function - make sure that the var elemPos = $('#bestvideos-2').offset().top is executed on dom ready
scrollSidebar: function (elemPos, scroll) {
var scroll2 = scroll;
if (scroll2 >= elemPos) {
$('#bestvideos-2').animate({
'margin-top': (scroll - 315) + 'px'
}, 0);
} else {
$('#bestvideos-2').css('margin-top', '0');
}
}
var elemPos = $('#bestvideos-2').offset().top
$(window).scroll(function () {
var scrollHeight = $(window).scrollTop();
Scroll.scrollSidebar(elemPos, scrollHeight);
})

scroll other scrollbars with scrollbar

i have 3 divs with scrollbars.
If i scroll in div 1 i want to scroll div 2 and 3 in the opposite direction.
The distance scrolled should be half the distance of div 1.
This is what i have now (small part, rest is in jsfiddle), which works for 1 div.
$("#textBox1").scroll(function () {
console.log("scroll 1");
var offset = $("#textBox1").scrollTop() - scrollPosTBox1;
var half_offset = offset/2.0;
disable1 = true;
if(disable2 == false) {
$("#textBox2").scrollTop(scrollPosTBox2 - half_offset);
}
if(disable3 == false) {
$("#textBox3").scrollTop(scrollPosTBox3 - half_offset);
}
disable1 = false;
});
However, if i try to get the same for the other 2 divs then i can't scroll anything anymore.
This is because div 1 triggers div 2 and div 2 triggers back to div 1 for example.
I tried to fix this with the disable code but it doesn't help.
Can someone help me?
http://jsfiddle.net/XmYh5/1/
No disrespect to #EmreErkan and #Simon for their effort. Here's a no-click version of this.
var $boxes = $("#textBox1,#textBox2,#textBox3"),
active;
$boxes.scrollTop(150);
// set initial scrollTop values
updateScrollPos();
// bind mouseenter:
// 1) find which panel is active
// 2) update scrollTop values
$boxes.mouseenter(function () {
active = this.id;
updateScrollPos();
});
// bind scroll for all boxes
$boxes.scroll(function (e) {
$this = $(this);
// check to see if we are dealing with the active box
// if true then set scrolltop of other boxes relative to the active box
if(this.id == active){
var $others = $boxes.not($this),
offset = $this.scrollTop()-$this.data("scroll"),
half_offset = offset / 2;
$others.each(function(){
$this = $(this);
$this.scrollTop($this.data("scroll") - half_offset);
});
}
});
// utility function:
// assign scrollTop values element's data attributes (data-scroll)
function updateScrollPos() {
$boxes.each(function(){
$this = $(this);
$this.data("scroll",$this.scrollTop());
});
}
Fiddle
You can use a variable to determine active textbox with .mousedown() and do the trick if it's active;
var activeScroll = '';
$("#textBox1").on('mousedown focus mouseenter', function () {
activeScroll = 'scroll1';
}).scroll(function () {
if (activeScroll == 'scroll1') {
console.log("scroll 1");
var offset = $("#textBox1").scrollTop() - scrollPosTBox1;
var half_offset = offset / 2.0;
$("#textBox2").scrollTop(scrollPosTBox2 - half_offset);
$("#textBox3").scrollTop(scrollPosTBox3 - half_offset);
}
});
You can check your updated jsFiddle here.
Finally got a dynamic solution for this, was more complex than I thought but I think I got it:
http://jsfiddle.net/XmYh5/14/
var initialTop = 150,
factor = 2;
$(".textBox")
.addClass('disabled')
.scrollTop(initialTop)
.on('scroll', function () {
var $this = $(this);
if(!$this.is('.disabled')) {
this.lastOffset = this.lastOffset || initialTop;
var offset = $this.scrollTop(),
step = (offset - this.lastOffset) / factor;
$this.siblings().each( function() {
var $this = $(this),
offset = $this.scrollTop() - step;
$this.scrollTop(offset);
this.lastOffset = offset;
});
this.lastOffset = offset;
}
})
.on('mouseenter', function() {
$(this).removeClass('disabled').siblings().addClass('disabled');
});

.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');
}

How to check if a DIV is scrolled all the way to the bottom with jQuery

I have a div with overflow:scroll.
I want to know if it's currently scrolled all the way down. How, using JQuery?
This one doesn't work: How can I determine if a div is scrolled to the bottom?
Here is the correct solution (jsfiddle). A brief look at the code:
$(document).ready(function () {
$('div').on('scroll', chk_scroll);
});
function chk_scroll(e) {
var elem = $(e.currentTarget);
if (elem[0].scrollHeight - elem.scrollTop() == elem.outerHeight()) {
console.log("bottom");
}
}
See this for more info.
function isScrolledToBottom(el) {
var $el = $(el);
return el.scrollHeight - $el.scrollTop() - $el.outerHeight() < 1;
}
This is variation of #samccone's answer that incorporates #HenrikChristensen's comment regarding subpixel measurements.
Since it works without jQuery like that :
var isBottom = node.scrollTop + node.offsetHeight === node.scrollHeight;
I do :
var node = $('#mydiv')[0]; // gets the html element
if(node) {
var isBottom = node.scrollTop + node.offsetHeight === node.scrollHeight;
}
You can do that by
(scrollHeight - scrollTop()) == outerHeight()
Apply required jQuery syntax, of course...
Here is the code:
$("#div_Id").scroll(function (e) {
e.preventDefault();
var elem = $(this);
if (elem.scrollTop() > 0 &&
(elem[0].scrollHeight - elem.scrollTop() == elem.outerHeight())) {
alert("At the bottom");
}
});
Since 2012 Firefox contains the scrollTopMax property. If scrollTop === scrollTopMax you're at the bottom of the element.
Without jquery, for onScroll event
var scrollDiv = event.srcElement.body
window.innerHeight + scrollDiv.scrollTop == scrollDiv.scrollHeight
For me $el.outerHeight() gives the wrong value (due to the border width), whereas $el.innerHeight() gives the correct one, so I use
function isAtBottom($el){
return ($el[0].scrollHeight - $el.scrollTop()) == $el.innerHeight();
}

Categories