2

What I'm trying to do is push new ".post" (which have been retrieved by the infinite-scroll script) into my array which holds all of the ".post".

Full Code: https://gist.github.com/sxxfi/8296403
Live Demo: http://soof-show.tumblr.com (navigate through posts with keyboard arrow keys)

Initially, five ".post" show up in the ".content" div, then each time the 'MORE' button is clicked it adds 5 more ".post" to ".content".

The problem is the array length doesn't count for the new ".post" so it keeps looping the first 5.

Thank you in advance if you're able to help solve this.

Javascript:

 $(document).ready(function(){
 // I N F I N I T E S C R O L L
 $('.content').infinitescroll({
 navSelector : ".navigation",
 nextSelector : ".navigation .next_page",
 itemSelector : ".content .post",
 behavior: "twitter"
 });
 $(window).unbind('.infscr');
 $('.next_page').click(function(){
 $(document).trigger('retrieve.infscr');
 return false;
 });
 $(document).ajaxError(function(e,xhr,opt){
 if (xhr.status == 404) $('.next_page').remove();
 });
 // I M A G E S L I D E R
 var obj = $(".post");
 var arr = $.makeArray(obj);
 var lastDiv = $(".post");
 var indexNum = arr.length-1;
 $(window).keydown(function (e) {
 if (e.keyCode == 37) { // L E F T
 lastDiv.fadeOut(800);
 indexNum--;
 if (indexNum == -1) indexNum = arr.length-1; //if goes beyond first post, goto the last post
 $(arr[indexNum]).appendTo(".postslide").fadeIn(800);
 // C E N T E R
 var width = $(arr[indexNum]).width();
 var marginLeft = width / 2;
 $(arr[indexNum]).css('margin-left', -marginLeft);
 }
 if (e.keyCode == 39) { // R I G H T
 $('#instruction').fadeOut();
 lastDiv.fadeOut(800);
 indexNum++;
 if (indexNum == arr.length) indexNum = 0; //if goes beyond last post, goto the first post
 $(arr[indexNum]).appendTo(".postslide").fadeIn(800);
 // C E N T E R
 var width = $(arr[indexNum]).width();
 var marginLeft = width / 2;
 $(arr[indexNum]).css('margin-left', -marginLeft);
 }
 });
 });

UPDATE:

 $(document).ready(function(){
 // I N F I N I T E S C R O L L
 $('.content').infinitescroll({
 navSelector : ".navigation",
 // selector for the paged navigation (it will be hidden)
 nextSelector : ".navigation .next_page",
 // selector for the NEXT link (to page 2)
 itemSelector : ".content .post",
 // selector for all items you'll retrieve
 behavior: "twitter"
 });
 // kill scroll binding
 $(window).unbind('.infscr');
 // manual click 
 $('.next_page').click(function(){
 $(document).trigger('retrieve.infscr');
 return false;
 });
 // remove the paginator done
 $(document).ajaxError(function(e,xhr,opt){
 if (xhr.status == 404) $('.next_page').remove();
 });
 // I M A G E S L I D E R
 var obj = $(".post");
 var indexNum = obj.length-1;
 var lastDiv = $(".post");
 $(window).keydown(function (e) {
 if (e.keyCode == 37) { // L E F T
 lastDiv.fadeOut(800);
 indexNum--;
 if (indexNum == -1) indexNum = obj.length-1; //if goes beyond first post, goto the last post
 $(obj[indexNum]).appendTo(".postslide").fadeIn(800);
 // C E N T E R
 var width = $(obj[indexNum]).width();
 var marginLeft = width / 2;
 $(obj[indexNum]).css('margin-left', -marginLeft);
 }
 if (e.keyCode == 39) { // R I G H T
 $('#instruction').fadeOut();
 lastDiv.fadeOut(800);
 indexNum++;
 if (indexNum == obj.length) indexNum = 0; //if goes beyond last post, goto the first post
 $(obj[indexNum]).appendTo(".postslide").fadeIn(800);
 // C E N T E R
 var width = $(obj[indexNum]).width();
 var marginLeft = width / 2;
 $(obj[indexNum]).css('margin-left', -marginLeft);
 }
 });
 });
asked Jan 7, 2014 at 20:03
18
  • 1
    I don't see where you're adding anything to the array. Commented Jan 7, 2014 at 20:09
  • @Barmar That's what I need help with, I'm unsure how to add to the array. I tried using push but that didn't work so I took it out the code. Commented Jan 7, 2014 at 20:15
  • I also don't see where you're getting the 5 new posts, so I can't figure out where you want to add the new elements to the array. Should I be looking at the live demo instead of the question to see what you're talking about? Commented Jan 7, 2014 at 20:22
  • @Barmar Yeah if you 'inspect element' via Chrome on the live demo you can see the new posts being generated in the "content" div Commented Jan 7, 2014 at 20:23
  • Does the infinite scroll widget let you supply a callback when the new elements have been added? That's where you need to add to the array. Commented Jan 7, 2014 at 20:29

2 Answers 2

3

jQuery selectors query the DOM at the moment the jQuery function is called, so they select the elements matching the selector at the given moment.

Even if new elements that should match your selector are added to the DOM later, they will not get selected as nothing notifies jQuery to reevaluate the selector, and even if something did, jQuery has no inner state holding all the jQuery objects created. This means that the selectors are not live.

Your main problem here is that you never update the array of selected DOM elements so even when new elements are added to the DOM you still loop over the original array.

Updating the jQuery object is easy, you just need to recall obj = $(".post") at any time in your code, and the obj variable gets updated. We just need to figure out when to call it.

Since you are using Infinite Scroll to load new elements we need to look at it's documentation, or since the documentation currently has large holes in it, it might be better to look at the source code.

Look at the infinitescroll function:

$.fn.infinitescroll = function infscr_init(options, callback) {
 ...

The second parameter is called callback and upon examining the source code and the way this parameter is passed and called, you can see that it will be called after new elements were retrieved. Let's try this parameter out:

$('.content').infinitescroll({
 navSelector : ".navigation",
 // selector for the paged navigation (it will be hidden)
 nextSelector : ".navigation .next_page",
 // selector for the NEXT link (to page 2)
 itemSelector : ".content .post",
 // selector for all items you'll retrieve
 behavior: "twitter"
}, function () {
 obj = $(".post");
 console.log("Current length is: " + obj.length); //this line is not necessary, just here for debug purposes.
});

Assuming that variable obj already exists this will update it's value.

Note that in a previous version of this answer I called this callback function recountPosts(). I have decided later that it can simply be an inline anonymous function since it will be only called from one place.

You are nearly finished, but... you have other variables depending on this selector. Namely arr and lastDiv. Upon examining your source code it seems that none of them are necessary:

  1. arr is an array made from the elements selected by the jQuery object and although the jQuery object is not an array, it has a zero based numeric indexer and a length property just like an array, so you can substitute arr with obj anywhere in your code.

  2. lastDiv has a misleading name. It holds the same elements as obj not just the last. Therefore it can also be substituted with obj.

So the part where you declare your variables can be shrunk to:

// I M A G E S L I D E R
var obj = $(".post");
var indexNum = obj.length-1;

I have also looked at your keydown event handler. There is a lot of duplicated code in it. This is prone to introduce bugs later on (in fact you already have a bug: if you press left instead of right at the start, the initial picture of the keyboard will not disappear).

Let me restructure it for you a little bit:

$(window).keydown(function (e) {
 if (e.keyCode === 37) { // L E F T
 indexNum--;
 }
 if (e.keyCode === 39) { // R I G H T
 indexNum++;
 }
 if (e.keyCode === 37 || e.keyCode === 39) {
 $('#instruction').fadeOut();
 obj.fadeOut(800);
 if (indexNum === -1) {
 indexNum = obj.length-1; //if goes beyond first post, goto the last post
 }
 if (indexNum === obj.length) {
 indexNum = 0; //if goes beyond last post, goto the first post
 }
 $(obj[indexNum]).appendTo(".postslide").fadeIn(800);
 var width = $(arr[indexNum]).width();
 var marginLeft = width / 2;
 $(obj[indexNum]).css('margin-left', -marginLeft);
 }
});

And it is done!


No, of course it is not done. It is far from done, there is still many, many things to improve.

  • Like removing the ugly recalculation of margin-left on every keypress and solve it with a few lines of css. As a thumb rule: if something can be solved in css instead of javascript it should be.

  • Or like calling infinitescroll(...) on $(".postslide") instead of $(".content") and removing the completely unnecessary .appendTo(".postslide") from before fadeIn(), as the elements are already appended and it is a waste of resouces to reappend them.

  • Or moving constants, like the 800 ms fade length to the beginning of the code giving them a variable like var fadeLength = 800; and use it.

And I'm pretty sure there are lot of other things that could be done better, but that is just how programming is :)

Have fun coding!

EDIT:

So you also want the more button only to appear if you are at the end of the slides. You have written this:

if (indexNum === 14){
 $('.next_page').html('<img src="http://long_url/">');
}

Why do you hard-code 14 into the source? Why do you even care on how many pictures are loaded and how many will load? The given part of the code need only to know if you are at the end. If you have a variable on how many slides are loaded, you can easily check if you are at the last index. You do have that variable, the obj.length:

if (indexNum === obj.length-1){
 $('.next_page').html('<img src="http://long_url/">');
}

This way the more button will appear on the last slide not caring if it's the 30th, 45th or 9001st.

You would also need this button to disappear after new content has been loaded. This goes into the callback of infinitescroll:

function () {
 obj = $(".post");
 console.log("Current length is: " + obj.length); //this line is not necessary, just here for debug purposes.
 $('.next_page').html('');
}

An other thing you could do is instead of showing the more button, you could trigger the more event from code. This way the new slides get loaded automatically when the user reaches the last slide.

Also this only needs to happen if there are actually more slides to load. You can check the current state's isDone property, which tells you if the "infinite scroll is done" so there are no more elements to load.

$('.content').data('infinitescroll').options.state.isDone === false

So instead of the part that shows the more button, you could just write this:

if (indexNum === obj.length-1 && !$('.content').data('infinitescroll').options.state.isDone){
 $(document).trigger('retrieve.infscr');
}
answered Jan 7, 2014 at 20:28

27 Comments

Thank you, that helped simplify my code. However it is still looping the first five posts, even after adding recountPosts(); to the handler of the MORE button.
I can't see the obj variable updated anywhere in the live demo you linked. Are you sure you update it? Also you should know, that you don't even need the lastDiv variable and can simply call obj.fadeOut(800);
I was trying different things with the live demo, I just updated it back now, let me know what you see
Nonono. You added the line in question AFTER the return! It will never be executed! Also since the event is an async call, the given line will possibly run before the pictures are loaded.
Oh it seems it was not clear what I intended. Now you don't trigger the event. I'll try to clear up my answer.
|
0

Problem:

You are setting value as soon as page loads and not updating it when scroll happens or at other key bindings

Solution:

 // I M A G E S L I D E R
 var obj = $(".post");
 var arr = $.makeArray(obj);
 var lastDiv = $(".post:last");
 var indexNum = arr.length-1;
 $(window).keydown(function (e) {
 obj = $(".post");
 arr = $.makeArray(obj);
 lastDiv = $(".post:last");
 indexNum = arr.length-1;
 //other code
 });
answered Jan 7, 2014 at 20:05

5 Comments

Just call obj = $(".post"); again.
obj isn't an array, it's a jQuery object.
The OP said my array which holds all of the ".post".
In your answer you re-assign arr and indexNum, but you don't re-assign obj first.
@RohitAgrawal the slider gets stuck on the first post with the updated code.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.