.scroll() is only running once - javascript

I'm trying to create a small snippet for a sticky sidebar with jQuery. For some reason, the function inside of .scroll() is only running once instead of running at every scroll event. What could I be doing wrong?
var sticky = $('#sticky');
var staticSticky = $(sticky).offset().top;
$(window).scroll(moveEl(sticky, staticSticky));
function moveEl(element, staticElToTop){
if( $(window).scrollTop() >= staticElToTop ){
$(element).css('top', $(window).scrollTop() + 'px');
}
}
See here for the entire attempt: http://codepen.io/ExcellentSP/pen/GqZwVG
The code above is not fully functional. I'm just trying to get the scroll event to work properly before I continue.

You need to wrap content of your your moveEl method into the function (to be returned for $(window).scroll()) like this:
var sticky = $('#sticky');
var staticSticky = $(sticky).offset().top;
$(window).scroll(moveEl(sticky, staticSticky));
function moveEl(element, staticElToTop) {
return function() {
if( $(window).scrollTop() >= staticElToTop ){
$(element).css('top', $(window).scrollTop() + 'px');
}
}
}
Explanation:
The key difference is that you call a function and it returns undefined by default, so it equals to $(window).scroll(undefined). Since you actually called it, you see it's fired only once which is obvious.
As soon as you return a function within moveEl method, .scroll() gets a handler, so it becomes $(window).scroll(handler). So it will work now as expected.

In doing that $(window).scroll(moveEl(sticky, staticSticky));, you ask to javascript to execute the function. You don't pass its reference.
$(window).scroll(function(){
moveEl(sticky, staticSticky);
});

Related

Make script run once per pageload

How do I make the following, run only once per pageload...
$(window).scroll(function(){
if($(document).scrollTop()>=$(document).height()/5)
$("#spopup").show("slow");else $("#spopup").hide("slow");
});
Here is what I use to close the pop up in case that makes a difference...
function closeSPopup(){
$('#spopup').hide('slow');
}
http://codepen.io/john84/pen/vgWVRp
I was reading up on the .one() function but am unsure as where to put it
http://api.jquery.com/one/
At the end of the function call, redefine the function.
$(window).scroll(function(){
if($(document).scrollTop()>=$(document).height()/5)
$("#spopup").show("slow");else $("#spopup").hide("slow");
$(window).scroll(function(){});
});
The first time they scroll, it will execute the function (based on your if/else) and after completing that action, it clears the javascript linked to on scroll
there is many ways to do this. i think setting a flag is going to work :
var isopen = false;
$(window).on('scroll', function(){
if($(document).scrollTop() >= $(document).height()/5 && isopen == false) {
$("#spopup").show("slow");
isopen = true;
$(window).off('scroll');
}
});

prevent javascript from loading more than once [duplicate]

This question already has an answer here:
Running function on window scroll once in jQuery
(1 answer)
Closed 6 years ago.
I was trying to run on scroll load more but I want it to only run once. The code below keeps on running each time the scroll reaches bottom of page.
I need to add something to the code
if ($(window).scrollTop() == $(document).height() - $(window).height() && x==24 ){
but i don't know what to add to make it so that it only runs once or so that the code is prevented from running more than once.
<script>
if ($(window).scrollTop() == $(document).height() - $(window).height() && x==24 ){
});
});
</script>
so basically my question is how to prevent javascript from running more than once.
If the scroll event is only needed to call the function once, you can then remove the scroll event from $(window) using .off() within if statement.
body {
height: 400px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
<div id="div1">div </div>
<script>
x = 24;
$(document).ready(function() {
$(window).on("scroll.once", function() {
if ($(window).scrollTop() == $(document).height() - $(window).height() && x == 24) {
$(window).off("scroll.once");
$("#div1").append(123);
}
});
});
</script>
I have a simple but not too elegant solution where you can add a boolean flag before the function and check the boolean state before executing the codes in your function. Change to the desire boolean state after the first call of your function so that you can prevent it from being called again. For example:
<script>
var called = false;
function myFunc(){
if (!called){
//your code here
called = !called;
}
}
</script>
It will not be the best answer out there but still, hope it helps =)

jQuery Plugin breaks with this.each

I'm writing a plugin and trying to wrap the function inside of an each method but it breaks the plugin. If the block content is not wrapped within "this.each" plugin works. I understand that in order to pass multiple selectors I need to "return this.each" or not? I also want to eliminate the need to use the selector inside the plugin e.g "#the_lead", instead use "this".
(function($) {
$.fn.scroll_lead = function (options) {
var defaults = {
speedup: 500
};
var options = $.extend({}, defaults, options);
return this.each(function () {
var $window_height = $(window).height();
var $document_height = $(document).height();
var $hide_lead;
$(window).scroll(function () {
var $scrollTop = $(window).scrollTop();
if (!$hide_lead) {
if ($scrollTop > ($document_height / 2)) {
$("#the_lead").slideDown(options.speedup);
} else {
$("#the_lead").slideUp(500, function () {
$(this).hide();
});
}
}
});
$('#hide_lead').click(function (e) {
$(this).parent().parents('div').hide();
hide_lead = true;
e.preventDefault();
});
});
};
})(jQuery);
Few things;
Assign $(this) to $this and use it inside any function inside your plugin, said in the tutorial, http://docs.jquery.com/Plugins/Authoring.
return this.each(function () {
var window_height = $(window).height();
var document_height = $(document).height();
var hide_lead;
$this = $(this);
$(window).scroll(function () {
var scrollTop = $(window).scrollTop();
if (!hide_lead) {
if (scrollTop > (document_height / 2)) {
$this.slideDown(options.speedup);
} else {
$this.slideUp(500, function () {
//$(this).hide();
});
}
}
});
$this.click(function (e) {
$(this).parent().parents('div').hide();
hide_lead = true;
e.preventDefault();
});
});
Try to avoid manipulation of parent objects inside your plugin, including $(window), and $(document).
It is ok to read properties of window and document, but if it is manipulated in your plugin, it will be maniuplated by number of times of your selector.
In your code, because you use this.each, you are binding scroll function of window several times. For example, $("div").scroll_lead() will bind 'scroll' method to window as many as tags of your document. The same applies to $(document) and all parent elements of plugin target.
If possible and it is your intention, use element scroll method, not window scroll.
To get scroll value $(el).scrollTop()
To scroll down, $(el).scrollTop(NUMBER)
To bind onScroll method, $(el).scroll( functtion() {...} )
Hope it helps
Firstly, your syntax is incorrect.
each would not exist on this as this is the context of the function, and not an element that jquery knows about.
try $(this).each which would get you closer.
remember Jquery.Each cannot iterrate over something that is not an object or array, so make sure that what you are trying to achieve makes sense.
What are you trying to achieve here?

document ready after dom manipulation

I'm doing an application with Phonegap and I'm using a self-built slide transition to change the pages.
It works like this:
Every page is a div with 100% height and width, so if I change the Page, I set the next div right to the currently active and slide both to the left side.
Now to the Problem: the sliding works fine, but it's executed before the content of the right div is completely loaded. So the right div slides in empty, and only after a few hundred miliseconds the content will appear.
I tried it with document.ready, but as I've read this event is only executed the first time the DOM is loaded.
Does anybody know how I can wait for the DOM to be completely rendered again after I've manipulated the DOM with Javascript?
In your case, you can pick one element in the content of the next div and keep checking it with $(...).length. If the value is > 0, the DOM is loaded and you can change the page.
You may want to try this function:
Function.prototype.deferUntil = function(condition, timeLimit) {
var ret = condition();
if (ret) {
this(ret);
return null;
}
var self = this, interval = null, time = ( + new Date());
interval = setInterval(function() {
ret = condition();
if (!ret) {
if (timeLimit && (new Date() - time) >= timeLimit) {
// Nothing
} else {
return;
}
}
interval && clearInterval(interval);
self(ret);
}, 20);
return interval;
};
Usage:
(function() {
console.log('Next page loaded');
}).deferUntil(function() {
return $('#nextDiv').length > 0;
}, 3000);
The above example will check the div that has id="nextDiv" in every 20ms (not longer than 3 seconds). When the div is loaded, it will show 'Next page loaded' in the console.
You can try on this fiddle
There is a DOMNodeInserted event that is supposed to work like document.ready but for individual DOM nodes. But it is deprecated and has lots of issues. StackOverflow users found a good alternative to it that works quite well in all mobile browsers: Alternative to DOMNodeInserted
Here is a function that will trigger a callback once all images matching a jquery selector have finished loading
Js Fiddle Sample
//css
input {width: 420px;}
//html
<div id="container"></div>
<input type="text" value="http://goo.gl/31Vs" id="img1">
<br><input type="text" value="http://wall.alafoto.com/wp-content/uploads/2010/11/Fractal-Art-Wallpapers-09.jpg" id="img2">
<br><input type="text" value="http://pepinemom.p.e.pic.centerblog.net/ssg8hv4s.jpg" id="img3">
<br><input type="button" value="Load images" name="loadImages" id="btn">
<div id="message"></div>
//javascript
//Call a function after matching images have finished loading
function imagesLoadedEvent(selector, callback) {
var This = this;
this.images = $(selector);
this.nrImagesToLoad = this.images.length;
this.nrImagesLoaded = 0;
//check if images have already been cached and loaded
$.each(this.images, function (key, img) {
if (img.complete) {
This.nrImagesLoaded++;
}
if (This.nrImagesToLoad == This.nrImagesLoaded) {
callback(This.images);
}
});
this.images.load(function (evt) {
This.nrImagesLoaded++;
if (This.nrImagesToLoad == This.nrImagesLoaded) {
callback(This.images);
}
});
}
$("#btn").click(function () {
var c = $("#container"), evt;
c.empty();
c.append("<img src='" + $("#img1").val() + "' width=94>");
c.append("<img src='" + $("#img2").val() + "' width=94>");
c.append("<img src='" + $("#img3").val() + "' width=94>");
evt = new imagesLoadedEvent("img", allImagesLoaded);
});
function allImagesLoaded(imgs) {
//this is called when all images are loaded
$("#message").text("All images loaded");
setTimeout(function() {$("#message").text("");}, 2000);
}
You could use jQuery ajax to load the content, and on success run a function with the slide.
$("#page1").load('page2.html', function() {
//do your custom animation here
});
Althoug I'm not completely sure how you're loading the content. Is it static (Already there but just not visible?) Or is it loaded with ajax?
EDIT: You could just do a small .delay() or setTimeout with a few millisec, and then animate the sliding.
I had a similar problem making a masonry site responsive. I use window.onload which waits for all elements to complete loading before initialising masonry.js. I also placed the window.onload inside .onchange function and it fired everytime the viewport resized.
I am sure applying similar principles will solve your problem.
try once
$(window).bind('load',function(){
//code
});
Maybe you can set an event on your div.
myDiv.onafterupdate = myHandler;
function myHandler() {
// Do here what you want to do with the updated Div.
}
Does this help you?
In jquery you could use $() just after your DOM manipulation code.
$(function(){
//code that needs to be executed when DOM is ready, after manipulation
});
$() calls a function that either registers a DOM-ready callback (if a function is passed to it) or returns elements from the DOM (if a selector string or element is passed to it)
You can find more here
difference between $ and $() in jQuery
http://api.jquery.com/ready/

Resize script appends object, does it over and over again

I have the following code in a JavaScript file:
$(document).ready(function() {
detectscreen();
});
$(window).resize(function(){
detectscreen();
});
function windowWidth() {
if(!window.innerWidth) {
// user is being a git, using ie
return document.documentElement.clientWidth;
} else {
return window.innerWidth;
}}
function detectscreen() {
if (windowWidth()>1280) {
$('body').append('<div id="gearsfloat"></div>');
}}
Basically, what it does is append an object to the end of the document if the width is less than 1280 pixels, however what this does is append it every single time the page is resized.
I don't think I can use the once function because it would only run it once and then the next time it is resized, it's dead. Anything I can do?
NOTE: I, in fact, DO want it to be checked on the resize of the page, but the effect is that it happens over and over again.
if (windowWidth()>1280 && !$('gearsfloat')) {
$('body').append('<div id="gearsfloat"></div>');
}
The above (by Jason) works does not work but then it won't delete it when it gets less than 1280. Is there anything I can do?
Keep track of whether the element exists or not, and add/remove it when the condition changes. That way you will only add it once, it will be removed when it shouldn't be there, and you don't do any unneccesary adding or removing:
var gearsExists = false;
function detectscreen() {
var shouldExist = windowWidth() > 1280;
if (shouldExist != gearsExists) {
if (shouldExist) {
$('body').append('<div id="gearsfloat"></div>');
} else {
$('#gearsfloat').remove();
}
gearsExists = shouldExist;
}
}
if (windowWidth()>1280 && !$('gearsfloat')) {
$('body').append('<div id="gearsfloat"></div>');
}
Check if the element already exists first?
if you dont want the function to be called when the window is resized, then why are you binding the resize function?
wont the document ready function always be called before the resize function anyway, so you are guaranteed to have your element appended?

Categories

Resources