I am developing a website using PHP, MySQL, JavaScript (JQuery + Ajax), I used customer scroll function and scrollbars, the problem is that if I load data with ajax the scroll function generates hundreds of errors saying:"Uncaught TypeError: Cannot read property 'top' of undefined" from the scroll function which is:
function Scroll() {
var contentTop = [];
var contentBottom = [];
var winTop = $(window).scrollTop();
var rangeTop = 200;
var rangeBottom = 500;
$('.navbar-collapse').find('.scroll a').each(function () {
contentTop.push($($(this).attr('href')).offset().top);//The error rises from this line
contentBottom.push($($(this).attr('href')).offset().top +
$($(this).attr('href')).height());
})
$.each(contentTop, function (i) {
if (winTop > contentTop[i] - rangeTop) {
$('.navbar-collapse li.scroll')
.removeClass('active')
.eq(i).addClass('active');
}
})
}
and also the website starts blinking when I scroll to the end, I tried checking if the "contentTop.push($($(this).attr('href')).offset()" is not null, errors get disappear but web page still blinks at the end of the page.
If you can help, please share your answer, Thanks in Advance.
var winTop = $(window).scrollTop();. This line forces window to go to top of the page. This might be the cause for blinking effect.
Related
By the way, sorry for my English. Please edit for me :)
I'm using https://youtube.github.io/spfjs/ for fast navigation like one page application. But when SPF.js call spf.navigate(url) I get redirected to another page.
For sample when I scroll down my website a bit, click <a class="spf-link" href="http://example.com/nextpage"> next page is loading normally without refresh, but scroll Y position is not saving and I'm getting to the top. That really annoys users when they just want to stay at the current scrolling.
Is there any configurable state or position whenspf.navigate() is called by SPF.js? or I have to do some trick to do it.
Answer is here
You can use session storage to store the position then get back to the
position when the page is reloaded, like this:
$(window).scroll(function() {
sessionStorage.scrollTop = $(this).scrollTop();
});
$(document).ready(function() {
if (sessionStorage.scrollTop != "undefined") {
$(window).scrollTop(sessionStorage.scrollTop);
}
});
For SPF.js user with vanilla javascript, this should do a trick :)
let xCoord = 0;
let yCoord = 0;
document.addEventListener('spfrequest', () => {
xCoord = window.pageXOffset;
yCoord = window.pageYOffset;
});
document.addEventListener('spfdone', () => {
window.scrollTo(xCoord, yCoord);
});
Background
I am trying to create an infinite scrolling table inside a fixed position div. The problem is that all the solutions I come across use the window height and document scrollTop to calculate if the user has scrolled to the bottom of the screen.
Problem
I have tried to create a jQuery plugin that can calculate if a user has scrolled to the bottom of a fixed div with overflow: scroll; set.
My approach has been to create a wrapper div (the div with a fixed position and overflow: scroll) that wraps the table, I also place another div at the bottom of the table. I then try calculate if the wrapper.scrollTop() is greater than the bottom div position.top every time the wrapper is scrolled. I then load the new records and append them to the table body.
$.fn.isScrolledTo = function () {
var element = $(this);
var bottom = element.find('.bottom');
$(element).scroll(function () {
if (element.scrollTop() >= bottom.position().top) {
var tableBody = element.find("tbody");
tableBody.append(tableBody.html());
}
});
};
$('.fixed').isScrolledTo();
See Example http://jsfiddle.net/leviputna/v4q3a/
Question
Clearly my current example is not correct. My question is how to I detect when a user has scrolled to the bottom of a fixed div with overflow:scroll set?
Using the bottom element is a bit clunky, I think. Instead, why not use the scrollHeight and height to test once the scrollable area has run out.
$.fn.isScrolledTo = function () {
var element = this,
tableBody = this.find("tbody");
element.scroll(function(){
if( element.scrollTop() >= element[0].scrollHeight-element.height()){
tableBody.append(tableBody.html());
}
});
};
$('.fixed').isScrolledTo();
EDIT (12/30/14):
A DRYer version of the plugin might be much more re-usable:
$.fn.whenScrolledToBottom = function (cback_fxn) {
this.on('scroll',this,function(){
if( ev.data.scrollTop() >= ev.data[0].scrollHeight - ev.data.height()){
return cback_fxn.apply(ev.data, arguments)
}
});
};
Plugin Usage:
var $fixed = $('.fixed'),
$tableBody = $fixed.find("tbody");
$fixed.whenScrolledToBottom(function(){
// Load more data..
$tableBody.append($tableBody.html());
});
I have modified your code to handle the scroll event with a timer threshold:
$.fn.isScrolledTo = function () {
var element = $(this);
var bottom = element.find('.bottom');
$(element).scroll(function(){
if (this.timer) clearTimeout(this.timer);
this.timer=setTimeout(function(){
if( element.scrollTop() >= bottom.position().top){
var tableBody = element.find("tbody");
tableBody.append(tableBody.html());
}
},300);
});
};
$('.fixed').isScrolledTo();
The issue you are having is that as you scroll, new scroll event is being generated. Your code might have other issues, but this is a start.
Ok I will try to make it simple.
1) I have 3 links that execute an Ajax Request and update a div with some content.
The DIV
<div id="content-to-update"></div>
The 3 links that update #content-to-update
example 1
example 2
example 3
Each link update the div #content-to-update with the content just below with one parameter named CODE
The the div #content-to-update is updated with the code below.
var loading = false;
$(window).scroll(function () {
var winTop = $(window).scrollTop();
var docHeight = $(document).height();
var winHeight = $(window).height();
//if user reach bottom of the page
if (!loading && (winTop / (docHeight - winHeight)) > 0.95) {
loading = true;
//the CODE parameter is different on each call from the links that I
//talked earlier.
$.get("/items/next/?list_name=" + CODE, function(data){
//executing some javascript to display next items
}).done(function() {
loading = false;
})
}
});
});
The problem is that it seams the browser keep all different version of the updated div.
Its like the old content is not erased before the new content is added.
If I click on the first link and scroll I get the right items OK!.
Then if I click on the second link, when I scroll I get the Item twice (duplicated- it calls the code from the previous ajax call)
Then if I click on the third link, when I scroll I get the Item 3 times. (it calls the code from the 2 previous ajax call )
When I use the Chrome debugger I see that it goes first in the code that have received the parameter EXAMPLE_1 then it goes in the code that has received the parameter EXAMPLE_2 etc
But this code should has been overridden by the call of the EXAMPLE_2 link.
It is difficult to explain I don't know if someone understand what Im trying to explain but I give it a try :) and again sorry for my english.
Thanks
I'm a bit picky about POST and GET, so even though Wayne is technically correct, the fact that you are retrieving data makes your use of GET the right way of doing it.
The way around caching is either by using jQuery's ajax method and setting cache to false, like so:
$.ajax({
url: "/items/next/?list_name=" + CODE,
type: 'GET',
success: function(data) {
$('#content-to-update').html(data);
},
cache:false,
error: function(e) {
alert("Server failure! Is the server turned off?");
}
});
You can also trick the browser by adding a random string to the end of the URL, which is what I usually do. Something like this:
$.get("/items/next/?list_name=" + CODE + '&cache_buster=' + (new Date()).getTime().toString(), function(data){
//executing some javascript to display next items
}).done(function() {
loading = false;
})
If you are using .html() to set the content, the error is definitely somewhere else. Ensure that you are not appending the new content to the div, which seems like what you are doing.
Also, your functions should act independently of one another. Your current process seems to support that, but your problem seem to suggest otherwise.
Try the suggestions first and if they don't work, post more code.
Update
Try this:
var loading = false;
function executeSomeAjax(CODE){
$(window).scroll(function () {
var winTop = $(window).scrollTop();
var docHeight = $(document).height();
var winHeight = $(window).height();
//if user reach bottom of the page
if (!loading && (winTop / (docHeight - winHeight)) > 0.95) {
loading = true;
//the CODE parameter is different on each call from the links that I
//talked earlier.
$.get("/items/next/?list_name=" + CODE, function(data){
//executing some javascript to display next items
}).done(function() {
loading = false;
})
}
});
});
}
As you can see, the variable loading is now a global variable. I suspect that it was a local variable in your original function and as a result was set to false anytime the function ran.
Making it a global variable should resolve your issue.
Hope this helps.
UPDATE
Ok this is the final working code thanks to everybody for helping me out !
I think the problem was coming from low memory on my computer. The code you see below was used yesterday and was not working.
Since I rebooted the computer this morning everything works like a charm. I have 4GO of memory and working with Grails 2.2.2 and Intellij IDEA Im often with 100Mo of memory left I guess this should have a side effect. I cant see other explanations.
If That can help anyone to read this post
var loading = false;
function nextProject(){
$('.logo').html('<img src="/images/ajax-loader-transparent.gif">');
$.ajax({
type:'GET',
url: "/project/next/",
data:"list_name=" + CODE,
beforeSend:function(){
console.log("loading : " + loading)
}
}).done(function(data) {
if(data != ""){
var arrayOfObjects = eval(data);
for(var i=0; i < arrayOfObjects.length; i++){
TrackManager.newTrack(btoa(arrayOfObjects[i].base64Params));
var projectMgr = new ProjectManager(arrayOfObjects[i].id);
projectMgr.socialShare();
<sec:ifNotLoggedIn >
projectMgr.runDeny();
</sec:ifNotLoggedIn>
<sec:ifLoggedIn >
projectMgr.runGranted(arrayOfObjects[i].likeUp, arrayOfObjects[i].inPlayList );
</sec:ifLoggedIn>
INC++;
}
loading = false;
$('.logo').html('<img src="/images/soundshare_logo_32.png">');
console.log(INC + "/" + PROJECT_COUNT );
}
}).fail(function(){
console.error("Ajax error!")
});
}
$(window).scroll(function(){
var winTop = $(window).scrollTop();
var docHeight = $(document).height();
var winHeight = $(window).height();
if ((winTop / (docHeight - winHeight)) > 0.95) {
if(INC < PROJECT_COUNT){
if(!loading){
loading = true;
nextProject()
}
}
}
});
I am trying to get a div to stick once it is scrolled out of view.
var jQ = jQuery.noConflict();
jQ(document).ready(function() {
var win = jQ(window);
var navTop = jQ('#navbar').offset().top;
win.scroll(function() {
jQ('#navbar').toggleClass('sticky', win.scrollTop() > navTop);
});
});
The problem is that with this code, navTop is not calculated correctly. If I calculate navTop in the scroll function it works as expected but with a horrible flickering effect which I assume is due to recalculating the value many times.
Why does it not calculate the value correctly after document is loaded?
The fix I used for this problem was to fire another scroll event once to calculate the navTop variable and it works ok now.
Final Code:
var jQ = jQuery.noConflict();
jQ(document).ready(function() {
var win = jQ(window);
var navTop;
jQ(document).one("scroll", function() {
navTop = jQ('#header').offset().top;
});
win.scroll(function() {
jQ('#navbar').toggleClass('sticky', win.scrollTop() > navTop);
});
});
I have a strange issue that might have to do with jQuery document ready. Below is an html and script block that contains the usual social networking scripts. The Javascript block below displays the dd_outer div on the left edge of the body div, and when the browser window is shrunk, the div is faded out and the dd_footer div is faded in. The fadein and fadeout between the two divs works OK.
The problem is two fold: one issue is when the browser window is full width (1200px+), the Facebook script will not load and display consistently; it sometimes appears and sometimes doesn't, sometimes after a page reload and sometimes doesn't. (No browser or .htaccess caching is involved). Only the Facebook share fails to show consistently; all other services show OK.
The second problem that when the browser window is narrow - 650 px or so, when the dd_outer div is not displayed and the dd_footer div is - the footer div will not show on a page reload until the browser window is moved the smallest amount. Then the the div will display, Facebook share and all. For a mobile device, this is a problem because the browser window will be narrow to begin with and shouldn't need to be "nudged" to make the dd_footer div display.
This problem may have come into play because I have adapted this code from a WordPress plugin that used options to set the position of the dd_outer div and scroll height. That's the reason for the variables above the document ready call.
Is this the issue with what seems to be a document ready issue?
How can the variables be integrated into the script itself? It doesn't matter if they are hardcoded; I can change them when needed.
I'd throw this in a jsfiddle to demo but the divs won't realistically float with the window resizing.
I haven't included the CSS for clarity.
This is the html and social script block:
<div class='dd_outer'><div class='dd_inner'><div id='dd_ajax_float'>
<div class="sbutton"><script src="http://connect.facebook.net/en_US/all.js#xfbml=1"></script><fb:like layout="box_count" show_faces="false" font=""></fb:like></div>
<div class="sbutton">
Tweet<script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script></div>
<div class="sbutton"><script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script><g:plusone size="tall"></g:plusone></div>
<div class="sbutton"><script src="http://platform.linkedin.com/in.js" type="text/javascript"></script>
<script type="IN/Share" data-counter="top"></script></div>
</div></div></div>
In the footer is <div id="dd_footer">that contains the same social scripts as above</div> and are faded in and out by the script below:
This is the jQuery that positions the dd_outer social services to the left and fades it out and fades in the dd_footer div.
<script type="text/javascript">
var dd_top = 0;
var dd_left = 0;
var dd_offset_from_content = 70; var dd_top_offset_from_content = 10;
jQuery(document).ready(function(){
var $floating_bar = jQuery('#dd_ajax_float');
var $dd_start = jQuery('#dd_start');
var $dd_end = jQuery('#dd_end');
var $dd_outer = jQuery('.dd_outer');
// first, move the floating bar out of the content to avoid position: relative issues
$dd_outer.appendTo('body');
dd_top = parseInt($dd_start.offset().top) + dd_top_offset_from_content;
if($dd_end.length){
dd_end = parseInt($dd_end.offset().top);
}
dd_left = -(dd_offset_from_content + 55);
dd_adjust_inner_width();
dd_position_floating_bar(dd_top, dd_left);
$floating_bar.fadeIn('slow');
if($floating_bar.length > 0){
var pullX = $floating_bar.css('margin-left');
jQuery(window).scroll(function () {
var scroll_from_top = jQuery(window).scrollTop() + 30;
var is_fixed = $dd_outer.css('position') == 'fixed';
if($dd_end.length){
var dd_ajax_float_bottom = dd_end - ($floating_bar.height() + 30);
}
if($floating_bar.length > 0)
{
if(scroll_from_top > dd_ajax_float_bottom && $dd_end.length){
dd_position_floating_bar(dd_ajax_float_bottom, dd_left);
$dd_outer.css('position', 'absolute');
}
else if ( scroll_from_top > dd_top && !is_fixed )
{
dd_position_floating_bar(30, dd_left);
$dd_outer.css('position', 'fixed');
}
else if ( scroll_from_top < dd_top && is_fixed )
{
dd_position_floating_bar(dd_top, dd_left);
$dd_outer.css('position', 'absolute');
}
}
});
}
});
jQuery(window).resize(function() {
dd_adjust_inner_width();
});
var dd_is_hidden = false;
var dd_resize_timer;
function dd_adjust_inner_width() {
var $dd_inner = jQuery('.dd_inner');
var $dd_floating_bar = jQuery('#dd_ajax_float')
var width = parseInt(jQuery(window).width() - (jQuery('#dd_start').offset().left * 2));
$dd_inner.width(width);
var dd_should_be_hidden = (((jQuery(window).width() - width)/2) < -dd_left);
var dd_is_hidden = $dd_floating_bar.is(':hidden');
if(dd_should_be_hidden && !dd_is_hidden)
{
clearTimeout(dd_resize_timer);
dd_resize_timer = setTimeout(function(){ jQuery('#dd_ajax_float').fadeOut(); }, -dd_left);
jQuery('#dd_footer').fadeIn();
}
else if(!dd_should_be_hidden && dd_is_hidden)
{
clearTimeout(dd_resize_timer);
dd_resize_timer = setTimeout(function(){ jQuery('#dd_ajax_float').fadeIn(); }, -dd_left);
jQuery('#dd_footer').fadeOut();
}
}
function dd_position_floating_bar(top, left, position) {
var $floating_bar = jQuery('#dd_ajax_float');
if(top == undefined) top = 0 + dd_top_offset_from_content;;
if(left == undefined) left = 0;
if(position == undefined) position = 'absolute';
$floating_bar.css({
position: position,
top: top + 'px',
left: left + 'px'
});
}
</script>
jQuery .ready() does not wait for iframes and other external media to load. These social buttons tend to work by inserting an iframe. The load event does wait for iframes etc, so you could try using that event instead, i.e.
jQuery(window).load(function () {
/* put the code you had inside .ready() here */
});
The problem comes with your idea: $(document).ready() fires when the DOM is ready, not when all scripts are ready!
an idea would be to search for trigger of that social-APIs you are using or just delay your calculations (e.g. via setTimeout).
Keep in mind that they are asyncron, even if you specify "async" on the script-tag to be false, you still dont know when they will activate or are finished.
I suggest to use the standard DOM event window.onload if you want to make sure that all the external assets, scripts, images, etc. are loaded first before you do something:
window.onload = function () {
// your script that needs to run after all the external assets are loaded
}
Reference: https://developer.mozilla.org/en-US/docs/DOM/window.onload
I just ran into similar problems with the facebook script... I just used the integration in the HEAD-section with javascript and added an "asynchronous"-attribute to the javascript-embedding script which then fires an asynchronous "heeey, facebook is ready now, too"-event to my jQuery-eventqueue...
I can't help you in detail, because I don't totally understand what you WANT to do and would reorganize the whole code A LOT... so - contact me private (email/skype) or try figuring out... I used that lines of code: pastie.org/private/9m4b9eet1dzzkl6duqpkrg