Initialize fotorama with images loaded by JSON/ajax - javascript

I tried to search but i can help with this ...
My html code is:
//<![CDATA[
window.onload=function(){
$("[data-instagram]").each(function(el) {
$.getJSON({
method: 'GET',
url: $(this).data("instagram"),
dataType: 'jsonp',
success: function(response) {
$('.fotorama').append('<img src="' + response.thumbnail_url + '">');
$('.fotorama').fotorama();
}
});
})
}//]]>
<!-- 1. Link to jQuery (1.8 or later), -->
<script type="text/javascript" src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<!-- fotorama.css & fotorama.js. -->
<link href="http://cdnjs.cloudflare.com/ajax/libs/fotorama/4.6.4/fotorama.css" rel="stylesheet"> <!-- 3 KB -->
<script src="http://cdnjs.cloudflare.com/ajax/libs/fotorama/4.6.4/fotorama.js"></script> <!-- 16 KB -->
<span data-instagram="https://api.instagram.com/oembed/?url=https://www.instagram.com/p/BNijakrAX5y/?taken-by=nba"></span>
<span data-instagram="https://api.instagram.com/oembed/?url=https://www.instagram.com/p/BNlZYpDgwr_/?taken-by=nba"></span>
<span data-instagram="https://api.instagram.com/oembed/?url=https://www.instagram.com/p/BNlTnGFAqQ4/?taken-by=nba"></span>
<span data-instagram="https://api.instagram.com/oembed/?url=https://www.instagram.com/p/BNlFOilgZ3b/?taken-by=nba"></span>
<!-- 2. Add images to <div class="fotorama"></div>. -->
<div class="fotorama" data-auto="false">
</div>
<!-- 3. Enjoy! -->
As you can see, in html i have few <span> elements with atribute data-instagram. Script found each span element with data-instagram and load instagram images via JSON/ajax into <div class="fotorama"></div>
I tried to initialize fotorama gallery with, but it looks like it wont initialize properly.
I tried to folow http://fotorama.io/customize/initialization/ to turn off auto initialization by adding data-auto="false" atribute to fotorama div, and $('.fotorama').fotorama(); into script code but it doesnt work.
How can i make this work?
Thank you, sorry for bad english.

You migh be aware of the async. nature of javascript so i will explain it straightforward , note that i've removed window.onload and some unrelated stuff for simplicity
$("[data-instagram]").each(function(el) {
$.getJSON({
//extra stuff hidden
success: function(response) {
$('.fotorama').append('<img src="' + response.thumbnail_url + '">');
$('.fotorama').fotorama(); //1
}
});
});
$('.fotorama').fotorama(); // 2
setTimeout(function(){ $('.fotorama').fotorama(); },3000); // 3
Your code scans over all data-instagram takes its value and uses that to make an async. call to instagram on success of which you call fotogram to initialize your slide but the problem is that since you're loading many images fotorama initialization gets run more than once(on each success).After first initialization any further attempt would only mess up the slider.Note that if you put it outside[2] foreach that wouldn't work either because at this point even the first request would be in pending state.
So what can you do? Well the simplest(and bad :P too) way would be to remove [1] use a timeout and initialize say after 3 seconds ( meanwhile show a loader gif) see [3] problem here is you cannot trust the network so a better way would be to use a counter , initial value of which would be equal to the no. of calls want to make. On each success callback decrement it by 1 as well as checking if it's 0 .When it is 0 you fire up fotogram
var count = $("[data-instagram]").length;
$("[data-instagram]").each(function(el) {
$.getJSON({
// other stuff
success: function(response) {
$('.fotorama').append('<img src="' + response.thumbnail_url + '">');
count--;
if(count === 0 )
$('.fotorama').fotorama(); //this was the last pending call,all images loaded. Initialize
}
});
});

Related

Jquery/Ajax/CSS show/hide div after form subimit with Ajax

I am having an issue showing a hidden DIV after posting a form with Ajax. The setup is like so;
index.html - My content and form is loaded into a div from a resource page
<div id="inst_stud_resource_change_success" style="display: none;">
<div class="greenbox">
Deleted!
</div>
</div>
<div id="inst_stud_resources_getpackages">
<script type="text/javascript">
$("#inst_stud_resources_getpackages").fadeOut("slow").load('/inst/stud/resources/getpackages?inst_id={{ inst.id }}&stud_id={{ stud.id }}').fadeIn('slow');
</script>
</div>
/inst/stud/resources/getpackages.html - this page has the form, and the content needed for the DIV on index.html
The Jquery is used like so on getpackages.html;
<script type="text/javascript">
$(document).ready(function() {
$('#delete_package').on('submit', function(event) {
$.ajax({
data : {
stud_id : $('#stud_id').val(),
pck_id : $('#pck_id').val()
},
type : 'POST',
url : '{{ url_for('mod_inst_stud.delete_package2') }}'
})
.done(function(data) {
if (data.error) {
$('#errorAlert').text(data.error).show(); // IGNORE haven't gotten this far
$('#successAlert').hide(); // IGNORE haven't gotten this far
}
else {
$("#inst_stud_resources_getpackages").hide(); // doesn't seem to do anything
$('#inst_stud_resource_change_success').show(); // never shows the div on Index.html
$('#inst_stud_resource_change_success').delay(3200).hide(); // never hides, since it is never shown..
$("#inst_stud_resources_getpackages").load('/inst/stud/resources/getpackages?inst_id={{ inst.id }}&stud_id={{ stud.id }}').show(); // this seems to work since the div from index.html is updated with the new content from getpackages.html
}
});
event.preventDefault();
});
});
Any ideas what is going on? I'm not really that verse in web development, my background is more in python/flask, not Jquery/Ajax/CSS.
I found the solution..
else {
$("#inst_stud_resources_getpackages").hide();
$('#inst_stud_resource_change_success').fadeIn(1000);
$('#inst_stud_resource_change_success').delay(2600).fadeOut(3500);
$("#inst_stud_resources_getpackages").delay(7200).fadeIn().load('/inst/stud/resources/getpackages?inst_id={{ inst.id }}&stud_id={{ stud.id }}');
}
Apparently there is a queue in Jquery, however .load() will always go to the front. So It was negating the order of my calls. I had to move some lines around, and add an animation to .load() in order to delay it from loading right way. Seems to be working well now.

Url click counter, only registering one unique click counter from one browser

I want to make my post url to update the total click by registering it one unique click per browser everytime it is(the url) clicked, in simpler words, i want my total clicks to be unique.
I have found the method for url clicking here
http://jsfiddle.net/donejs/qYdwR/
//html
<!-- YOUR CODE HERE -->
<div id="content"></div>
<!-- PUT ANY TEMPLATES YOU NEED HERE -->
<script id="main-template" type="text/mustache">
<h2>CanJS component example</h2>
<click-counter></click-counter>
</script>
//javascript
var Component = can.Component.extend({
tag: 'click-counter',
template: '<a href="javascript://" can-click="updateCount">' +
'Click Me</a>' +
'<p id="msg">Clicked {{count}} times</p>',
scope: {
count: 0,
updateCount: function() {
this.attr('count', this.attr('count') + 1);
}
}
});
$('#content').html(can.view('main-template', {}));
The example of what i wish to do can be seen here pingje.org (article post).
Help me gurus, point me where to start, ive been trying to achieve this for days now, any time you took to answer my question is highly appreciated.

Preventing reloading Ajax data when back-button is clicked in both Chrome and IE

I am currently working on a website in which the home page displays the most recent 10 blog entries. When I scroll down, and when I reach almost the end of the last item on screen, another 10 blog entries are automatically loaded, and so on (this is the infinite scrolling feature).
If a user clicks on any blog entry, then he/she is taken to another page to display the details about that blog entry. When the user clicks the back button, he/she is taken to the home page that has the entries displayed.
Please note, the home page loads the data using Ajax.
Assume the following scenario:
A user goes to the site, and entries 1 to 10 are loaded (via Ajax).
The user scrolls down, and the next 10 entires, specifically entries 11 to 20 are loaded (via Ajax as well). Note that the page now has entires 1 to 20 displayed.
The user scrolls further down, and now entries 21 through 30 are loaded, for a total of 1 through 30 blog entries being displayed on the page.
The user clicks on entry 25, and the page for entry 25 is displayed.
The user clicks the back button, and all items, 1 through 30 are displayed.
Now, if the users uses FireFox, Opera, or Safari, and when that user performs step 5 (i.e, clicks the back button to go back to the home page) then the blog entries are just displayed on screen, and without being re-loaded. However using IE and on Chrome, when the user clicks on the back button, the page is reloaded, and only items 1 through 10 are displayed.
I do not like the IE and Chrome behaviour. The user should see items 1 though 30. How can I ensure all browsers behave like FireFox?
Thanks.
Update
Here is the code I am using
First, hers is my html
<html>
<body>
<section id="content">
<section id="articles">
<!-- This section is filled by jQuery/Ajax -->
</section>
<section id="loading-spinner">
<img src="ajax-loader.gif" />
</section>
</section>
</body>
</html>
And here is my jQuery
/**
*
* This file uses a bunch of programming concepts, but the most important one is ensuring ajax calls run as a critical section
* ... (this means if ajax is already called, then another instance of JavaScript cannot get into the critical section)
*
* .. For more details, please read: http://stackoverflow.com/questions/22150960/critical-section-in-javascript-or-jquery
*
*/
load_more_posts = function () {
// If we almost reach the bottom of the document, then load more posts
if ( jQuery(window).scrollTop() >= jQuery(document).height() - jQuery(window).height() - 300) {
// If the value of the promise is not pending, then we can call the load_posts function (the load_posts function will change the status to pending when it is executing the ajax request)
if (ajax_status.state() !== "pending") {
load_posts();
}
}
};
function load_posts() {
ajax_status = jQuery.ajax({
type: 'post',
dataType: 'json',
beforeSend: function(xhr) {
if(jQuery.data(document.body, 'load_page') == false) {
xhr.abort();
}
else {
// Show the spinner
jQuery('#loading-spinner').visible();
}
},
url: '../link/to/get_poasts.php',
data: {
action: 'load_posts',
js_query_data: query_data,
js_page: jQuery.data(document.body, 'page_to_load')
},
success: function (response) {
if (response.isSuccessful) {
var number_of_post_items = response.posts_array.length;
for (var i = 0; i < number_of_post_items; i++) {
// If the item is already returned from the database and posted. then we skip it, otherwise, keep insert a new record
if (jQuery('#articles').find('#article-' + response.posts_array[i].post_id).length == 0) {
// Add 'article'
jQuery('#articles').append('<article id="article-' + response.posts_array[i].post_id + '"></article>');
// More code here to add details about each article, such as title, excerpt...etc.
}
}
// Increase the value of the page to load by 1, and save it.
page = jQuery.data(document.body, "page_to_load");
page = page + 1;
jQuery.data(document.body, "page_to_load", page);
jQuery(window).on('scroll', load_more_posts);
}
else {
// Display error message
jQuery('#articles').append('<div>' + response.message + '</div>');
// Make sure no further AJAX requests are made
jQuery.data(document.body, 'load_page', false);
}
}
}).always(function() {
// Hide the spinner
jQuery('#loading-spinner').invisible();
});
return ajax_status;
}
// Create a new promise. This will be used to ensure that no two calls hit the critical section at the same time
// ... (the critical section in this case is the time when we retrieve data from the database. We only want one call at a time)
var ajax_status = new jQuery.Deferred();
jQuery(document).ready(function() {
// Hide the loading spinner first
jQuery('#loading-spinner').invisible();
// We resolve the promise, making sure it is ready (this is an intial state)
ajax_status.resolve();
// Initial values that are used
jQuery.data(document.body, 'page_to_load', 1);
// This parameter is used to stop loading pages when no more items are available to be displayed
jQuery.data(document.body, 'load_page', true);
// Initial loading of poasts
load_posts();
// Enable on scrolling to load more pasts (to allow infinite scrolling)
jQuery(window).on('scroll', load_more_posts);
});
You'll find some information about how it works in the "good" browsers in this answer:
Is there a cross-browser onload event when clicking the back button?
Here you'll find a way to emulate it in IE:
Differences in Internet Explorer and Firefox when dynamically loading content then going forward and back
Not quite sure why it doesn't work in Chrome though.
Just so everyone knows, here is the solution I came up with that is consistent in all browsers. Unfortunately this solution requires a reloead/refresh button to reload the data. I tried to avoid that but could not. Until both IE and Chrome tackle the bfcache issue, I will stick to this solution.
First, here is the new html
<html>
<body>
<section id="content">
<a id="refresh">
<img src="link/to/refresh.png" title="Refresh" alt="refresh" />
</a>
<section id="articles">
<!-- This section is filled by jQuery/Ajax -->
</section>
<section id="loading-spinner">
<img src="ajax-loader.gif" />
</section>
</section>
</body>
</html>
And this is the javascript
/**
*
* This file uses a bunch of programming concepts, but the most important one is ensuring ajax calls run as a critical section
* ... (this means if ajax is already called, then another instance of JavaScript cannot get into the critical section)
*
* .. For more details, please read: http://stackoverflow.com/questions/22150960/critical-section-in-javascript-or-jquery
*
*/
load_more_posts = function () {
// If we almost reach the bottom of the document, then load more posts
if ( jQuery(window).scrollTop() >= jQuery(document).height() - jQuery(window).height() - 300) {
// If the value of the promise is not pending, then we can call the load_posts function (the load_posts function will change the status to pending when it is executing the ajax request)
if (ajax_status.state() !== "pending") {
load_posts();
}
}
};
function load_posts() {
ajax_status = jQuery.ajax({
type: 'post',
dataType: 'json',
beforeSend: function(xhr) {
if(jQuery.data(document.body, 'load_page') == false) {
xhr.abort();
}
else {
// Show the spinner
jQuery('#loading-spinner').visible();
}
},
url: '../link/to/get_poasts.php',
data: {
action: 'load_posts',
js_query_data: query_data,
js_page: sessionStorage.getItem("page_to_load")
},
success: function (response) {
if (response.isSuccessful) {
var number_of_post_items = response.posts_array.length;
for (var i = 0; i < number_of_post_items; i++) {
// If the item is already returned from the database and posted. then we skip it, otherwise, keep insert a new record
if (jQuery('#articles').find('#article-' + response.posts_array[i].post_id).length == 0) {
// Add 'article'
jQuery('#articles').append('<article id="article-' + response.posts_array[i].post_id + '"></article>');
// More code here to add details about each article, such as title, excerpt...etc.
var history_session = get_history_session_name();
var history = sessionStorage.getItem(history_session);
var article_content = jQuery('#articles').find('#aarticle-' + response.posts_array[i].post_id)[0].outerHTML;
sessionStorage.setItem(history_session, history + article_content);
}
}
// Increase the value of the page to load by 1, and save it.
page = parseInt(sessionStorage.getItem("page_to_load"));
page = page + 1;
sessionStorage.setItem("page_to_load", page);
jQuery(window).on('scroll', load_more_posts);
}
else {
// Display error message
jQuery('#articles').append('<div>' + response.message + '</div>');
// Make sure no further AJAX requests are made
jQuery.data(document.body, 'load_page', false);
}
}
}).always(function() {
// Hide the spinner
jQuery('#loading-spinner').invisible();
});
return ajax_status;
}
function get_history_session_name () {
session_name = 'history___' + escape(location.href);
return session_name;
}
// Create a new promise. This will be used to ensure that no two calls hit the critical section at the same time
// ... (the critical section in this case is the time when we retrieve data from the database. We only want one call at a time)
var ajax_status = new jQuery.Deferred();
jQuery(document).ready(function() {
// Hide the loading spinner first
jQuery('#loading-spinner').invisible();
// We resolve the promise, making sure it is ready (this is an intial state)
ajax_status.resolve();
// This parameter is used to stop loading pages when no more items are available to be displayed
jQuery.data(document.body, 'load_page', true);
// Get the name of the history session
var history_session = get_history_session_name();
if (sessionStorage.getItem(history_session) === null) {
// Set the history session storage
sessionStorage.setItem(history_session, "");
// Initial values that are used
sessionStorage.setItem("page_to_load", 1);
// Load the posts
load_posts();
}
// Load from history when the back button is clicked
else {
jQuery('#articles').append(sessionStorage.getItem(history_session));
}
// Enable on scrolling to load more pasts (to allow infinite scrolling)
jQuery(window).on('scroll', load_more_posts);
// Reload data when the refresh button is clicked
// ... We are using a refresh button because if we go to a page that already had history, ...
// ... and even though we went to that page without using the back button (i.e, via links or directly via the url), ...
// ... then the history session will be displayed. ...
// ... Therefore a reload button is needed to overcome this problem if you like to reload data
jQuery("#refresh").click(function () {
// Reset/clear the articles section first
jQuery('#articles').html("");
// reset the 'load_page' variable
jQuery.data(document.body, 'load_page', true);
// Reset/clear the history session storage
sessionStorage.setItem(history_session, "");
// Start with loading page 1
sessionStorage.setItem("page_to_load", 1);
// Load the posts
load_posts();
});
});
Hope this helps.

Change the output automatically without refreshing

I have this code, it basically changes the word 'article' to 'articles' if the value is '0' or more than '1'. But the word only changes when the page is refreshed. Is it possible to automaticaly re-run the code at the same moment the value (quantity) changes?
<span id="quantity" class="simpleCart_quantity"></span> gets it's value from an external file, it is always a number (like 0, 1, 2, 3, etc.)
<span id="quantity" class="simpleCart_quantity"></span>
<span id="quantityText"></span>
<script type="text/javascript">
$(window).load(function()
{
var quantity = document.getElementById("quantity"),
quantityText = document.getElementById("quantityText");
if (parseInt(quantity.innerHTML, 10) === 1) {
quantityText.innerHTML = "article";
} else {
quantityText.innerHTML = "articles";
}
});
</script>
Thanks in advance!
Yes it's possible.
The direct and dirty way is to poll the server for changes at an interval. make a timer function that calls an ajax function that checks to see if the article count has changed.
something like:
setInterval(function(){
$.ajax({
url:"checkchanges.php",
dataType:"json",
type:'GET',
.success(function(data){
if (data.articlecount>1){ //pseudocode, it depends on how you get your data
$("#quantity").innerHTML="articles";
}
})
});
},3000);
Another method is to use a framework like meteor that wires the checking up for you.

How to avoid memory leaks during periodical AJAX requests on IE9

I've developed a webpage showing some statistics.
These statistics are refreshed periodically by AJAX requests using mootools 1.4.5.
Here ist the basic code:
<script type="text/javascript">
var statisticRequest = new Request.HTML({
url: theURL,
noCache: true,
onSuccess: function(responseTree, responseElements, responseHTML, responseJavaScript) {
$(responseTree[0]).replaces($('statisticContainer'))
}
})
function getCurrentStatistics() {
statisticRequest.get()
}
window.addEvent('domready', function(){
getCurrentStatistics.periodical(2000)
});
</script>
On FF all works fine but the IE9 continuously allocates memory until the machine is nearly freezed.
It looks like the garbage collector didn't remove the old DOM elements.
Using sIEve, I can see the increasing number of DOM elememts and the resulting memory usage.
What can I do to force the IE to remove the unused elements?
Edit:
Using destroy() as shown below will slow down the memory consumption but will not stop it completely. Removing Request.HTML had no further effect.
<script type="text/javascript">
var statisticRequest = new Request({
url: theURL,
noCache: true,
onSuccess: function(responseText, responseXML) {
var newStatistic = Elements.from(responseText)
var oldStatistic = $('statisticContainer')
newStatistic.replaces(oldStatistic)
oldStatistic.destroy()
}
})
function getCurrentStatistics() {
statisticRequest.get()
}
window.addEvent('domready', function(){
getCurrentStatistics.periodical(2000)
});
</script>
yes you can. look at the code for this:
https://github.com/mootools/mootools-core/blob/master/Source/Element/Element.js#L743-747
it will just replace it in the dom. it won't really do much in terms of GC - the old element still 'exists' - in case you want to re-attach it.
http://jsfiddle.net/rE3JH/
var foo = document.id('foo');
new Element('div').replaces(foo);
console.log(foo); // still an element, though not in the dom
call foo.destroy(); to properly GC - see https://github.com/mootools/mootools-core/blob/master/Source/Element/Element.js#L802-807
alternatively, update the parent of staticContainer - applying a change to innerHTML direct. also, keep in mind .empty() will dispose child nodes and not destroy them - for periodical stuff like yours, you need to be thorough as it can avalanche over time.
I've been playing about with this for a while. I think you're better to use something like mootools. It's well tested and is pretty small. It also gives you lots of extra features that are all cross browser compatible and don't need extensve testing.
I've run the following code for over two hours with no memory leaks. Hope it helps either the questioner or someone else searching stack overflow: First the index.html file:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Untitled Document</title>
<style>
img.slide{
border:1px solid black;
}
</style>
<script type="text/javascript" src="mootools/mootoolsCore.js">
</script>
<script type="text/javascript" src="mootools/mootoolsMore.js">
</script>
<script type="text/javascript">
function data(){
this.who="alan";
this.pageHolder=1;
this.lastPage = 4;
this.count = 0;
}
var newData = new data();
function changePage(newData,dirn){
newData.dirn = dirn;
saveData(newData);
}
function saveData(newData) {
//alert("reached save data");
var dataJSON = JSON.encode (newData);
var request = new Request.JSON({
method: 'post',
url: 'forwardDataJson.php',
data: {
json: dataJSON
},
onComplete: function(jsonObj) {
newData.pageHolder = jsonObj.pageHolder;
newData.count = jsonObj.count;
$("picHolder").set('html','<img class="slide" src ="OpeningSlide/Slide'+jsonObj.pageHolder+'.jpg"/>');
$("alertBox").set('html',jsonObj.alertGiven);
$("countme").set('html',jsonObj.count);
}
}).send();
};
function getCurrentStatistics() {
saveData(newData);
}
window.addEvent('domready', function(){
getCurrentStatistics.periodical(2000)
});
</script>
</head>
<body>
<div id="alertBox"></div>
<button type="button" onmousedown="changePage(newData,'backward')"/>backward</button>
<button type="button" onmousedown="changePage(newData,'forward')">forward</button>
<div id="picHolder"><img class="slide" src ="OpeningSlide/Slide1.jpg"/></div>
<div id="countme">0</div>
</body>
</html>
This looks for a series of images (Slide1.jpg, Slid2.jpg etc) and then displays them on the page. It checks every two seconds for a new bit of info and gets a counter number. Clicking forward or back makes the slides run through every 2 seconds. Not very exciting but it demonstrates the princilple of using AJAX and a server poll with Mootools.
You also need a server side script. Which in this case is:
<?php
if(get_magic_quotes_gpc()){
$test = stripslashes($_POST['json']);
}else{
$test = $_POST['json'];
}
$obj = json_decode($test);
$direction = $obj->{'dirn'};
$counter = $obj->{'count'};
++$counter;
$obj->{'count'} = $counter;
switch ($direction) {
case "forward":
if($obj->{'pageHolder'} < $obj->{'lastPage'}){
++$obj->{'pageHolder'} ;
}
break;
case "backward":
if($obj->{'pageHolder'} >1){
--$obj->{'pageHolder'} ;
}
break;
}
$reply = json_encode($obj);
echo $reply;
?>
You'll notice that I've used JSON for passing an object to and from the server. This is just for ease of coding. If you've never used it before it's the way to go (in my opinion at least) since it's straightforward to use once you get your head around it.
This example should work as is. You simply need a directory called OpeningSlide which should contain your jpg slides/images and of course the mootools libs.
Hope this helps
Incidently if you're wondering about the first few lines of the php code it's there to sort out issues with JSON and magic quotes. It adjusts for magic quotes being on or off at the server. You can remove it if you know your server setting.

Categories

Resources