Infinite Scroll + Swipe.js - javascript

Background:
I'm making a portfolio site utilising both Swipe.js and Infinite Ajax Scroll (JQ).
Problem:
When the content from extra pages is loaded into the current page, it is not processed by the already-loaded Swipe.js script. This means that the new content doesn't have it's mark-up changed (needed for the swipe functionality to work).
I think I need to get the Swipe.js script to fire after each page re-load. Would that fix it? Please explain this to me like I'm an 8yr old. JS is not a strong suit...
Demo:
http://hatchcreative.co.nz/tomo
You can see that as the page loads new content, the buttons on either side of the sliders no longer work.

Yes you're right, after the images are loaded you have to create a new Swipe instance on these new elements (as they weren't there at the beginning, when the page was loaded).
Based on the docs of infinite scroll you can use onRenderComplete.
So you had your jQuery.ias constructor like this:
jQuery.ias({
// ... your settings...
onRenderComplete: function(items) {
$(items).each(function(index, element) {
new Swipe(element);
});
}
});
This should work this way somehow, but I am not exactly sure; I haven't worked with these libraries yet.
Edit:
After some more inspection of your code, I saw you had some inline click handler like: onclick='two.prev();return false;'.
You need to remove this and add your onclick handle in the same onRenderComplete function.
onRenderComplete: function(items) {
var swipe;
$(items).each(function(index, element) {
swipe = new Swipe(element);
});
// find tags with the class 'forward' inside the current element and add the handler
$(element).find('.forward').on('click', function() {
swipe.next();
});
// ... also for previous
}
By the way: Usually you should provide a jsFiddle with your important code parts, so it's easier for us to get the problem, and the question is not getting obsolote when the linked page changes.

Related

shorthand for .load() ajax links with loader

here's the structure of the code: http://jsfiddle.net/ss1ef7sq/
although it's not really working at js fiddle but the code itself is working as i've tested it locally through firefox.
this is where i've based this on: http://html.net/tutorials/javascript/lesson21.php
jquery/ajax:
$('#ep-101').click(function(){$('.main-container').load('link.html #ep101').hide().fadeIn(800);});
$('#ep-102').click(function(){$('.main-container').load('link.html #ep102').hide().fadeIn(800);});
$('#ep-103').click(function(){$('.main-container').load('link.html #ep103').hide().fadeIn(800);});
$('#ep-104').click(function(){$('.main-container').load('link.html #ep104').hide().fadeIn(800);});
$('#ep-105').click(function(){$('.main-container').load('link.html #ep105').hide().fadeIn(800);});
so my question is, is there a way to make it like a shorter code where it can just get the value of those #10ns or assuming that there will be a different page with it's own nest of unique ids without typing them individually? there's still a lot i don't understand with ajax so i'd appreciate it if anyone can help & explain at least the gist of it as well.
i've looked around online but i'm really stuck. i also at least found out that it's possible to add transitions but the way it's coded there is that it will only have the transition for the incoming page & not the one that will be replaced. i also have a prob with page loaders effects but i'll save it for when i'm stuck there as well.
thanks in advance. =)
Use classes instead of id's. Set href attribute which you want to load on click and access it via $(this).attr('href').
<a class="load-me" href="link1.html">link 1</a>
<a class="load-me" href="link2.html">link 2</a>
...
Script:
$('.load-me').click(function(e){
e.preventDefault();
$('.main-container').hide().load($(this).attr('href'), function() {
// ...
$(this).fadeIn(800);
})
});
JSFiddle
If you need the load to wait container hiding animation, you could make it other way.
$('.load-me').click(function(e){
e.preventDefault();
// get the url from clicked anchor tag
var url = $(this).attr('href');
// fade out the container and wait for animation complete
$('.main-container').fadeOut(200, /* animation complete callback: */ function(){
// container is hidden, load content:
$(this).load(url, /* load complete callback: */ function() {
// content is loaded, show container up
$(this).slideDown(200);
});
});
});
JSFiddle

editing a Div that is around an iframe via jquery from within said iframe

I'm sure this sounds a little odd, but here's the background...
We utilize a company that loads their chat program, so we can support our customers, into our page. This is done via javascript and jquery, and creates a structure like this:
<div id="myid" style="...; right: 0px;..."><div><iframe></iframe></div></div>
There's a WHOLE lot more to that, but those are the important parts. Now the tool allows us to put custom scripting, which will be placed in the iframe. My goal is to just remove the "right: 0px", which I have done via the below code, but I don't want to put that code on every page that this tool integrates with. I would like to load it into the tool, and have it run when the iframe and divs are created.
working code on parent:
$(document).ready(function() {
function checkPos() {
$('#myId').each(function() {
var oldstyle = $('#myId').attr('style');
var newstyle = oldstyle.replace(' right: 0px;','');
$('#myId').attr('style', newstyle);
});
setTimeout(checkPos, 100);
};
$(document).ready(function() {
setTimeout(checkPos, 100);
});
});
Once placed in the code include method they provide, I have trouble having it wait until the div tag actually has the "right: 0px;" in its style tag. the only thing I need to run is the three lines in the $('#myId').each(function()
Basically, I need help with having the script in the iframe target the div that the iframe is nested in.
Assuming that whatever tool your using actually lets you pass in a custom script to the content rendered in the iframe (seems fishy to me), a better way of modifying the style in jquery is to use the css function:
$('#myId').css('right', '0px');
Notice I removed the $.each function as well. You are targeting an element with an id, so there isn't any need to iterate.
Edit:
Anyways, back to the problem of delaying execution to when the target, #myId, actually exists. If they are really injecting your javascript into their page (again, seems fishy), then attaching the above code to the $(document).ready() event should do the trick, as long as this listener is attached to their document.
If all else fails, try to use the waitUntilExists plugin, here:
Source:
https://gist.github.com/buu700/4200601
Relevant question:
How to wait until an element exists?

jQuery infinite-scroll Not Triggering

I'm making a simple little website to apply a different formatting style to Reddit posts, I'm trying to add the infinite-scroll jQuery plugin but it doesn't do anything. I tried following the (very simple) instructions on the infinite-scroll page and when it didn't do anything I thought I must have entered something wrongly, but then I just copy/pasted the code from the Masonry/Infinite-Scroll example and it still didn't work. Masonry is working perfectly (finally) but I just can't figure out what is wrong with infinite-scroll. I understand the basics of jQuery and JavaScript, but obviously not as much as most of you people, so could you please help me out and let me know what is wrong? My site is live at reddit.ymindustries.com.
Thanks heaps, you guys have rarely failed me so far.
YM
EDIT: If there aren't enough images to fill up the page on the homepage, visit reddit.ymindustries.com/r/aww for more images.
EDIT 2: I believe I located the issue, it is described here: https://github.com/paulirish/infinite-scroll/issues/5
Now to figure out a fix...
EDIT 3: Added a little bit of a hack in to make it sort of work, but it just seems to loop the second page endlessly now. Hmm...
I think your problem is actually css. Make your page longer that client area height. add more images to $container
Point is, botom edge of your $container need to pass bottom of window so scroll event fires so infinite scroll can react on this event and calculate weather or not edge is reached
BTW, in same cases, for instance, when I shrink my window, the example you set is working.
=== UPDATE ===
I found some time to play with infinitescroll and here is final working script, just set pathParse method in your script
$(function () {
var $container = $('#itemContainer');
$container.imagesLoaded(function () {
$container.masonry({
itemSelector:'.item'
});
});
$container.infinitescroll({
navSelector:'.navigation', // selector for the paged navigation
nextSelector:'.navigation #next', // selector for the NEXT link (to page 2)
itemSelector:'.item', // selector for all items you'll retrieve
bufferPx:40,
debug:true,
columnWidth:function (containerWidth) {
return containerWidth / 5;
},
loading:{
finishedMsg:'No more pages to load.',
img:'http://i.imgur.com/6RMhx.gif'
},
pathParse: function(path,page){
return $(this.nextSelector).attr("href");
}
},
// trigger Masonry as a callback
function (newElements) {
// hide new items while they are loading
var $newElems = $(newElements).css({ opacity:0 });
// ensure that images load before adding to masonry layout
$newElems.imagesLoaded(function () {
// show elems now they're ready
$newElems.animate({ opacity:1 });
$container.masonry('appended', $newElems, true);
});
//console.log("test (never fired :( )");
}
);
});
Now, since your next link will not update by it self (http://reddit.ymindustries.com/?after=t3_yh4av), you need to change the callback to pull out last element from ajax response and change next link... could be something like this
function (newElements) {
// hide new items while they are loading
var $newElems = $(newElements).css({ opacity:0 });
// ensure that images load before adding to masonry layout
// ======> if query parameter after=... is caring filename then do this
var lastImageUrl= $newElements[$newElements.length-1].attr("src");
var lastFileName= lastImageUrl.substring(lastImageUrl.lastIndexOf("/") +1, lastImageUrl.lastIndexOf("."));
$("#next").attr("href", "http://reddit.ymindustries.com/?after="+lastFileName);
$newElems.imagesLoaded(function () {
// show elems now they're ready
$newElems.animate({ opacity:1 });
$container.masonry('appended', $newElems, true);
});
//console.log("test (never fired :( )");
}
You also need to take care of wich version of infinite-scroll your using since if you use the ones that comes with masonry/isotope (version 2.0b2.110713), both need a little hack in order to call the function and not use the predefined array:
//old code, to be changed (line 489)
desturl = path.join(opts.state.currPage);
// new code
desturl = (typeof path === 'function') ? path(opts.state.currPage) : path.join(opts.state.currPage);
This is already fixed in the newer versions of infinite-scroll
I had the same problem with jQuery's "infinitescroll" and Masonry. You might just solve this by giving your page more initial items so that the plugin's scrolling detection kicks in.
In WordPress this is under the "Reading" settings. By default WordPress only opens 10 items at a time. You could increase that number to 100/page to be more sure the window will be full initially. I had some code here that was just horrible, turns out I just needed longer pages, not more code.
So it's difficult to test these plugins on large displays if you don't have enough images. Maybe the solution is to scale the images larger on large displays so you're more sure about getting your content below the fold.
If you think someone might get to your website with a really huge display, I'm not sure what the answer is other than showing more items/page and maybe adding $('#masonry').infinitescroll('retrieve'); to your footer to load an extra page just in case.

shadowbox stops working after jquery function call

I have a shadowbox script. When I load the page everything works fine, but when I call this jquery load function and then try to trigger the shadowbox by clicking on the image, the large image opens in new window instead.
Here's the code:
<link href="CSS/main.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="shadowbox-3.0.3/shadowbox.js"></script>
<script type="text/javascript">
Shadowbox.init();
</script>
<p id="compas"></p>
Any idea why this is happening?
EDIT
So, we finally get the bottom of this. 15 hours after first commenting on this issue, and at least 50 iterations later, we finally have identified what the problem is and how to fix it.
It actually struck me suddenly when I was creating local aaa.html and bbb.html on my server. That was when it hit me that the element nodes for the content that was being replaced was being removed altogether from the DOM when $.load() runs the callback function. So, once the #menu-home content elements were replaced, they were removed from the DOM and no longer had Shadowbox applied to them.
Once I figured this out, it was just a matter of a single web search and I found:
Nabble-Shadowbox - Reinit Shadowbox
Specifically, the response from mjijackson. What he describes is how to "restart" (reinitialize) Shadowbox using:
Shadowbox.clearCache();
Shadowbox.setup();
So once the #menu-home content was reloaded, what needs to happen is the Shadowbox cache needs to be cleared (essentially, shutting it down on the page), then the Shadowbox.setup() is run, which will detect the elements all over again. You don't run the Shadowbox.init() method again either.
I noticed that you had tried to copy/paste the Shadowbox.setup() in after the $.load(), at least sequentially in the code. However, this wasn't going to work, due to the cache clearing that needs to happen first, and primarily because the .clearCache() and .setup() functions need to be run after the $.load() completes (finishes and runs any callbacks). Those two functions need to be run in the $.load() callback handler; otherwise, you're running it's immediately, but the $.load() is asynchronous and will complete at some later time.
I'm going to go over some other changes I made, just so you understand what, why and wherefore.
Note, I'm not sure if you're familiar with <base>, but the following is at the top of the HEAD element:
<base href="http://62.162.170.125/"/>
This just let's me use the resource files on your computer. You'll not want to use this on your actual site more than likely. If you copy/paste, make sure and remove this line.
<div id="menu">
<ul>
<li><a id="menu-home" href="index.html" rel="http://jfcoder.com/test/homecontent.html">Home</a></li>
<li><a id="menu-services" href="services.html" rel="http://jfcoder.com/test/servicescontent.html">Services</a></li>
<li><a id="menu-tour" href="tour.html" rel="http://jfcoder.com/test/tourcontent.html">Tour</a></li>
<li><a id="menulogin" href="login.html">Login</a></li>
</ul>
</div>
Here, you'll notice I have a relative url in the HREF attribute, and a link to some pages on my server. The reason for the links to my server is that I couldn't access your aaa.html and bbb.html files through AJAX due to cross-site scripting limitations. The links to my website should be removed as well.
Now, the reason I'm using the rel attribute here is that I want allow for the links by way of the href attribute to continue to work in case the JS doesn't function correctly or there's some other error. If you have separate files, one for full HTML document and another for just the fragments, this is what you'll want to do. If you can serve both the full document AND the content-only from the linked file, then you probably don't need the rel attribute, but you'll need to manage the request so the server knows how to respond (full document or just the content part).
var boxInitialize = function(){
try {
if (!Shadowbox.initialized) {
Shadowbox.init();
Shadowbox.initialized = true;
} else {
Shadowbox.clearCache();
Shadowbox.setup();
}
} catch(e) {
try {
Shadowbox.init();
} catch(e) {};
}
};
All I've done here is create a central location for the initialization/setup requests. Fairly straightforward. Note, I added the Shadowbox.initialized property so I could keep track of if the Shadowbox.init() had run, which can only be run once. However, keeping it all in one spot is a good idea if possible.
I also created a variable function which can be called either as a regular function:
boxInitialize();
Or as a function reference:
window.onload = boxInitialize; // Note, no () at the end, which execute the function
You'll probably notice I removed the $() and replaced them with jQuery() instead. This can turn into a real nightmare if you end up with an environment with multiple frameworks and libraries competing for $(), so it's best to avoid it. This actually just bit me real good the other day.
Since we have a closure scope within the .ready() callback, we can take advantage of that to save several "private" variables for ow use at different times in the scripts execution.
var $ = jQuery,
$content = jQuery("#content"), // This is "caching" the jQuery selected result
view = '',
detectcachedview = '',
$fragment,
s = Object.prototype.toString,
init;
Note the , at the end of all but the last line. See how I "imported" the $ by making it equal to the jQuery variable, which means you could actually use it in that#.
var loadCallback = function(response, status, xhr){
if (init != '' && s.call(init) == '[object Function]') {
boxInitialize();
}
if (xhr.success()
&& view != ''
&& typeof view == 'string'
&& view.length > 1) {
$fragment = $content.clone(true, true);
cacheContent(view, $fragment);
}
};
This runs when the $.load() completes the process of the AJAX request. Note, the content returned in the request has already been placed on the DOM by the time this runs. Note as well that we're storing the actual cached content in the $content.data(), which should never be removed from the page; only the content underneath it.
var cacheContent = function(key, $data){
if (typeof key == 'string'
&& key.length > 1
&& $data instanceof jQuery) {
$content.data(key, $data.html());
$content.data(detectcachedview, true);
}
};
cacheContent() is one a method you may not want; essentially, if it was already loaded on a previous request, then it will be cached and then directly retrieved instead of initiating another $.load() to get the content from the server. You may not want to do this; if so, just comment out the second if block in the menuLoadContent() function.
var setContent = function(html){
$content.empty().html(html);
if (init != '' && s.call(init) == '[object Function]') {
boxInitialize();
}
};
What this does is first empty the $content element of it's contents/elements, then add the specified string-based markup that we saved earlier by getting the $content.html(). This is what we'll re-add when possible; you can see once the different links have been clicked and loaded, reclicking to get that to redisplay is really quick. Also, if it's the same request as currently loaded, it also will skip running the code altogether.
(We use $content like because it is a reference to a variable containing a jQuery element. I am doing this because it's in a closure-scope, which means it doesn't show up in the global scope, but will be available for things like event handlers.
Look for the inline comments in the code.
var menuLoadContent = function(){
// This is where I cancel the request; we're going to show the same thing
// again, so why not just cancel?
if (view == this.id || !this.rel) {
return false;
}
// I use this in setContent() and loadCallback() functions to detect if
// the Shadowbox needs to be cleared and re-setup. This and code below
// resolve the issue you were having with the compass functionality.
init = this.id == 'menu-home' ? boxInitialize : '';
view = this.id;
detectcachedview = "__" + view;
// This is what blocks the superfluous $.load() calls for content that's
// already been cached.
if ($content.data(detectcachedview) === true) {
setContent($content.data(view));
return false;
}
// Now I have this in two different spots; there's also one up in
// loadCallback(). Why? Because I want to cache the content that
// loaded on the initial page view, so if you try to go back to
// it, you'll just pickup what was sent with the full document.
// Also note I'm cloning $content, and then get it's .html()
// in cacheContent().
$fragment = $content.clone(true, true);
cacheContent(view, $fragment);
// See how I use the loadCallback as a function reference, and omit
// the () so it's not called immediately?
$content.load(this.rel, loadCallback);
// These return false's in this function block the link from navigating
// to it's href URL.
return false;
};
Now, I select the relevant menu items differently. You don't need a separate $.click() declaration for each element; instead, I select the #menu a[rel], which will get each a element in the menu that has a rel="not empty rel attribute". Again, note how I use menuLoadContent here as a function reference.
jQuery("#menu a[rel]").click(menuLoadContent);
Then, at the very bottom, I run the boxInitialize(); to setup Shadowbox.
Let me know if you have any questions.
I think I might be getting to the bottom of this. I think the flaw is the way you're handling the $.load() of the new content when clicking a menu item, coupled with an uncaught exception I saw having to do with an iframe:
Uncaught exception: Unknown player iframe
This Nabble-Shadowbox forum thread deals with this error. I'm actually not getting that anymore, however I think it came up with I clicked on the tour menu item.
Now, what you're doing to load the content for the menu items really doesn't make any sense. You're requesting an entire HTML document, and then selecting just an element with a class="content". The only benefit I can see for doing this is that the page never reloads, but you need to take another approach to how to get and display the data that doesn't involve downloading the entire page through AJAX and then trying to get jQuery to parse out just the part you want.
I believe handling the content loading this way is the root cause of your problem, hence the $.load() toggling of menu views breaks your page in unexpected ways.
Question: Why don't you just link to the actual page and skip all the $.load() fanciness? Speed-wise, it won't make that much of an impact, if any at all. It just doesn't make sense to use AJAX like this, when you could just link them to the same content without issue.
There are two alternatives that would allow you to prevent roundtrip page reloads:
Setup your AJAX calls to only request the .content portion of the markup if you have the ?contentonly=true flag in the URL, not the entire HTML document. This is how it's traditionally done, and is usually relative simple to do if you have a scripting environment.
$(".content").load('index.html?contentonly=true');
Then your server responds only with the content view requested.
Serve all of the content views within the same HTML document, then show as appropriate:
var $content = $('.content');
$content.find('.content-view').hide();
$content.find('#services-content').show();
It doesn't look like you have a whole lot of content to provide, so the initial page load probably won't have that much of an impact with this particular approach. You might have to look into how to preload images, but that's a very well known technique with many quality scripts and tutorials out there.
Either one of these techniques could use the #! (hashbang) technique to load content, although I believe there are some issues with this for search engines. However, here is a link to a simple technique I put together some time ago:
http://jfcoder.com/test/hash.html
Also, this is just a tip, but don't refer to your "content" element with a class, ie, .content. There should only be one content-displaying element in the markup, right? There's not more than one? Use an id="content"; that's what ID attributes are for, to reference a single element. classes are meant to group elements by some characteristic they share, so above when I .hide() the inline content views (see #2), I look for all of the class="content-view" elements, which are all similar (they contain content view markup). But the $content variable should refer to $('#content');. This is descriptive of what the elements are.
This worked for us, we made a site that used vertical tabs and called in the pages with our shadowbox images using jQuery.load
Just give all of your anchor tags the class="sbox" and paste this script in the header.
<script>
Shadowbox.init({
skipSetup:true,
});
$(document).ready(function() {
Shadowbox.setup($('.sbox'));//set up links with class of sbox
$('a.sbox').live('click',function(e){
Shadowbox.open(this);
//Stops loading link
e.preventDefault();
});
});
</script>
Note: we had to put the .sbox class on all our rel="shadowbox" anchors as well as the on the anchor for the tab that called the .load
Thanks to this guy-> http://www.webmuse.co.uk/blog/shadowbox-ajax-and-other-generated-content-with-jquery-and-javascript/
Well, based on Shem's answer, this is my solution.
Every click on specific class, setup and open shadowbox with elements from same class:
jQuery('.sb-gallery a').live('click',function(e){
Shadowbox.setup(jQuery('.sb-gallery a'));
Shadowbox.open(this);
//Stops loading link
e.preventDefault();
});
Thanks to all

How can I scroll to a page element in jQuery Mobile?

I have a long jQuery mobile page and would like to scroll to an element halfway down this page after the page loads.
So far I've tried a few things, the most successful being:
jQuery(document).bind("mobileinit", function() {
var target;
// if there's an element with id 'current_user'
if ($("#current_user").length > 0) {
// find this element's offset position
target = $("#current_user").get(0).offsetTop;
// scroll the page to that position
return $.mobile.silentScroll(target);
}
});
This works but then the page position is reset when the DOM is fully loaded. Can anyone suggest a better approach?
Thanks
A bit late, but I think I have a reliable solution with no need for setTimeout(). After a quick look into the code, it seems that JQM 1.2.0 issues a silentScroll(0) on window.load for chromeless viewport on iOS. See jquery.mobile-1.2.0.js, line 9145:
// window load event
// hide iOS browser chrome on load
$window.load( $.mobile.silentScroll );
What happens is that this conflicts with applicative calls to silentScroll(). Called too early, the framework scrolls back to top. Called too late, the UI flashes.
The solution is to bind a one-shot handler to the 'silentscroll' event that calls window.scrollTo() directly (silentScroll() is little more than an asynchronous window.scrollTo() anyway). That way, we capture the first JQM-issued silentScroll(0) and scroll to our position immediately.
For example, here is the code I use for deep linking to named elements (be sure to disable ajax load on inbound links with data-ajax="false"). Known anchor names are #unread and #p<ID>. The header is fixed and uses the #header ID.
$(document).bind('pageshow',function(e) {
var $anchor;
console.log("location.hash="+location.hash);
if (location.hash == "#unread" || location.hash.substr(0,2) == "#p") {
// Use anchor name as ID for the element to scroll to.
$anchor = $(location.hash);
}
if ($anchor) {
// Get y pos of anchor element.
var pos = $anchor.offset().top;
// Our header is fixed so offset pos by height.
pos -= $('#header').outerHeight();
// Don't use silentScroll() as it interferes with the automatic
// silentScroll(0) call done by JQM on page load. Instead, register
// a one-shot 'silentscroll' handler that performs a plain
// window.scrollTo() afterward.
$(document).bind('silentscroll',function(e,data) {
$(this).unbind(e);
window.scrollTo(0, pos);
});
}
});
No more UI flashes, and it seems to work reliably.
The event you're looking for is "pageshow".
I was digging a lot this issue, also at jQuery mobile official forum.
Currently it seems that there is no solution (at least for me).
I tried different events (mobileinit, pageshow) and different functions (silentscroll, scrolltop) as suggested above, but, as a result, I always have page scrolled until all images and html is finished loading, when page is scrolled to top again!
Partial and not really efficient solution is using a timer as suggested in comment to sgliser's answer; unfortunately with a timeout is difficult to know when page will be fully loaded and if scroll happened before that, it will scroll back to top at the end of load, while if it happens too long after page has fully loaded, the user is already scrolling page manually, and further automated scroll will create confusion.
Additionally, would be useful to have silentscroll or other function to address a specific id or class and not plain pixels, because with different browsers, resolutions and devices it may give different and not correct positioning of the scroll.
Hope someone will find a smarter and more efficient solution than this.

Categories

Resources