Event triggered before data is inserted into page (After AJAX call) - javascript

This site (http://nelation.net/) loads pages by using AJAX and pushState. AJAX retrieves the contents of section#body and the path for the new CSS.
The following code sends the AJAX request, retrieves the new contents of section#body (page contents) and a new CSS path. Then it inserts those into the page, and after that it calls the "pageLoad" event - That's the event I believe is triggered too early.
function loadPage(url) {
var target = document.getElementById("body");
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xhr.onreadystatechange = function() {
if (xhr.readyState < 4) {
target.innerHTML = "";
}
if (xhr.readyState == 4 && xhr.status == 200) {
// Function to decode the new page
var decodeEntities = (function() {
// this prevents any overhead from creating the object each time
var element = document.createElement('div');
function decodeHTMLEntities (str) {
if(str && typeof str === 'string') {
// strip script/html tags
str = str.replace(/<script[^>]*>([\S\s]*?)<\/script>/gmi, '');
str = str.replace(/<\/?\w(?:[^"'>]|"[^"]*"|'[^']*')*>/gmi, '');
element.innerHTML = str;
str = element.textContent;
element.textContent = '';
}
return str;
}
return decodeHTMLEntities;
})();
var resultJSON = JSON.parse(xhr.responseText);
var page = decodeEntities(resultJSON.page);
// Remove existing CSS and insert new one
$(".page-css").remove();
if (resultJSON.css != "none") {
$("<link/>", {
"class": "page-css",
rel: "stylesheet",
type: "text/css",
href: resultJSON.css
}).appendTo("head");
}
// Insert page contents, then trigger the pageLoad event
$(target).html(page);
$("body").trigger("pageLoad");
}
};
xhr.send();
}
// Detect link clicks, and make AJAX calls out of them + pushState.
$("body").on("click", '[data-ajax="true"]', function(event) {
event.preventDefault();
// detect which page has been selected
var newPage = $(this).attr("href");
if (newPage != window.location) {
window.history.pushState({path: newPage}, "", newPage);
}
loadPage(newPage);
});
The "pageLoad" event handler is found in this script. It re-executes most of the script, most notably the centerPlayButton_Featured() function if you're in the home page. That function resizes the overlay you see when you hover the image on the home page; It works fine when you load the page normally, but when you get to the page via AJAX it will not. The function is still executed (Logs to console), but I suspect it executes before the content is loaded properly into the page.
// DOES NOT RE-EXECUTE ON AJAX
function centerPlayButton_Featured() {
console.log("centerPlayButton_Featured() just executed");
var coverWidth = $("section.home-latest-release img.cover").width();
var coverHeight = $("section.home-latest-release img.cover").height();
$("section.home-latest-release div.cover-overlay").css({
"height": coverWidth + "px",
"width": coverHeight + "px"
});
}
$("body").click(function(e) {
if ($(e.target).hasClass("dropdown-text")) {
if ($(e.target).siblings(".menu").hasClass("open")) {
$(e.target).siblings(".menu").removeClass("open");
$(e.target).removeClass("open");
} else {
$(".dropdown .menu, .dropdown .dropdown-text").removeClass("open");
$(e.target).siblings(".menu").addClass("open");
$(e.target).addClass("open");
}
} else {
$(".dropdown .menu, .dropdown .dropdown-text").removeClass("open");
}
});
// RE-EXECUTES ON AJAX
$("body").on("pageLoad", function() {
$(function() {
// HOME PAGE
if ($("#body").children().hasClass("home-latest-release")) {
centerPlayButton_Featured();
$(window).on('resize', function() {
centerPlayButton_Featured();
});
}
// MUSIC PAGE
if ($("#body").children().hasClass("music-tracks")) {
//...
}
// CONTACT PAGE
$(function() {
if ($("#body").children().hasClass("contact")) {
$("textarea").bind("input", function() {
var offset = this.offsetHeight - this.clientHeight;
$(this).css("height", "auto").css("height", this.scrollHeight + offset);
});
}
});
});
});
$("body").trigger("pageLoad");
I appreciate any help/feedback. Go to the link to see more of the code, and tell me if you need to see the back-end. I apologize if the code is messy and comment-lacking. Thank you very much for the help.
Another problem I'm having is when you go to the music page, you may see all the dropdown menus fading out. Not as important as the main question, but help would be appreciated :)

The pageLoad event is not triggered because it is bound to the window load event:
$(window).bind("load", function() {
$("body").trigger("pageLoad");
});
load happens only once during the lifetime of a window, and that time has long passed when an ajax request can even start. Trigger pageLoad directly.
$(function () {...}) is executed once when the DOM has finished loading. Effectively, you are wrapping the code to be executed twice with the same event load which only happens once. Execute your setup code directly inside the pageLoad event handler. I'd write it like this:
function pageSetup () {
// HOME/MUSIC/CONTACT PAGE setup code
}
$("body").on("pageLoad", pageSetup);
pageSetup();

Related

HTML5 history API: cannot go backwards more than once

I have been trying to get my script working but apparently there is something wrong with it: when I try to go backwards with the browser back button, it stops at the first page backwards i.e. the second time I click the back button, does not work properly and instead updates the current page with itself.
Examples:
homepage -> second page -> third page -> second page -> second page -> second page (and so on)
homepage -> second page -> third page -> fourth page -> third page-> third page (and so on)
This instead works:
homepage -> second page -> homepage
Does anyone have a clue to what I am missing?
var domain = 'http://example.com/';
function updatePage(json){
var postData = JSON.parse(json);
// pushState
var url = domain + postData.url;
var title = postData.title;
document.title = title;
history.pushState({"page": url}, title, url);
// Clears some elements and fills them with the new content
// ...
// Creates an 'a' element that triggers AJAX for the next post
var a = document.createElement('a');
a.innerHTML = postData.next;
a.href = domain + postData.next;
document.getElementById('container').appendChild( a );
listenerAttacher( a );
// Creates another 'a' element that triggers AJAX for the previous post
a = document.createElement('a');
a.innerHTML = postData.previous;
a.href = domain + postData.previous;
document.getElementById('container').appendChild( a );
listenerAttacher( a );
}
function loadPost( resource ){
// Loads post data through AJAX using a custom function
loadHTML( resource, function(){
updatePage( this.responseText );
});
}
function listenerAttacher( element ){
// Adds a click listener to an element.
element.addEventListener('click', function(e){
e.preventDefault();
e.stopPropagation();
loadPost( this.href +'.json' );
return false;
},
false);
}
(function(){
history.replaceState({'page': window.location.href}, null, window.location.href);
// Adds the event listener to all elements that require it.
var titles = document.querySelectorAll('.post-title');
for (var i = 0; i < titles.length; i++){
listenerAttacher( titles[i] );
}
// Adds a popstate listener
window.addEventListener('popstate', function(e){
if ( e.state == null || e.state.page == domain ){
window.location = domain;
}
else {
loadPost( e.state.page + '.json' );
}
}, false);
})();
When you pressed back button, popstate event is fired and loadPost function is called. However in loadPost, history.pushState method is called again, which pushes the current page on the history stack again. Which explains why the first back button works and then it does not.
1) A quick fix is to check if the current state matches the state you are trying to push:
if (!history.state || history.state.page!=url)
history.pushState({ "page": url }, title, url);
2) Event better, you can add parameter to loadPost and updatePage functions to prevent unnecessary pushState calls:
function updatePage(json, disablePushState) {
...
// disablePushState is true when back button is pressed
// undefined otherwise
if (!disablePushState)
history.pushState({ "page": url }, title, url);
...
}
function loadPost(resource, disablePushState) {
// Loads post data through AJAX using a custom function
loadHTML(resource, function (responseText) {
updatePage(responseText, disablePushState);
});
}
...
window.addEventListener('popstate', function (e) {
if (e.state == null || e.state.page == domain) {
window.location = domain;
}
else {
loadPost(e.state.page + '.json', true);
}
return true;
});
Hope this help.

click event delay and pre loader animation

I want to combine two techniques in page transition. first, when user click a link , the click event should be delayed about 200ms and displaying css animation (ex: fading), after that the new page will be loaded normally. Then, for visitors with slow internet connection, they will be presented with pre-loader right after the first animation. The problem is, it seems both techniques doesn't fit each others. Any advice guys?
PS: Actually I inspired by these guys website : http://rsq.com/
here's my code:
$body = $("body");
$(document).on({
ajaxStart: function () {
$body.addClass("loading"); //for slow connection, visitors with faster connection may not notice this...
},
ajaxStop: function () {
$body.removeClass("loading");
}
});
$('a').on("click", function () {
if ($(this).attr('href') != '#') {
var href = $(this).attr('href');
$('#wrapper').addClass('fade_animation'); //animation right before new page request
setTimeout(function () {
window.location = href
}, 200);
return false;
$.post($(this).attr('href'));
}
});
I think this is enough:
Instead of:
return false;
$.post($(this).attr('href'));
do this
$.post($(this).attr('href'));
return false;
So the $.post gets executed.
I think i found a solution , but don't know if it's a good way, but it works.
$('a').on("click", function(){
if($(this).attr('href') != '#')
{
var href = $(this).attr('href');
$('#wrapper, footer').addClass('fading animation');
setTimeout(function() {
// window.location = href
$.post(window.location = href) // this line works :)
}, 200);
return false;
// $.post($(this).attr('href'));
}
});

How to use onhashchange with dynamic items

So, I have two select boxes on a webpage, but in different anchors (one on the page, the other in an iframe) and I'm trying to get the code to detect which anchor it's in, and then relay the selected value in that box to a link. Here's my code:
function locationHashChanged() {
if (location.hash === "#player") {
function setText(text) {
var selectVal = text;
var url = $('twitter').attr("href");
url = 'https://twitter.com/intent/tweet?button_hashtag=stream&text=Just enjoying ' + selectVal + ' on';
$('#twitter').attr("href", url);
}
}
if (location.hash === "#embeds") {
$(function () {
var $twitter = $('twitter');
$('#iframe').on('load', function () {
$(this).contents().find('#cds').change(function () {
var selectVal = $(this).val() || 'nothing much';
url = 'https://twitter.com/intent/tweet?button_hashtag=stream&text=Just enjoying ' + selectVal + ' on';
$('#twitter').attr("href", url);
}).change();
});
});
}
}
I know this is probably not right, or anywhere near right, but am I on the right track? I'm honestly a complete noob when it comes to javascript. Thanks in advance
Apart from what exactly your function looks like, it's not executed on hash change right now.
You use jQuery, so you can listen for hash change like this:
$(window).on('hashchange', function() {
// your locationHashChanged() function goes here
});
With this, every time the hash changes your function will be executed. The very base of your code is alright:
if (location.hash === "#player") {
// this is executed if hash changed to #player
}
if (location.hash === "#embeds") {
// this is executed if hash changed to #embeds
}
Although, inside your if blocks you declare functions (which doesn't make much sense here).
Also note that if the iframe is not from your domain, you won't be able to get any data from it. If that's the case, read more about same origin policy.

Javascript Queue commands

I have a Tab widget set up on my website using JQuery 1.7.2 and JQuery UI 1.10.3.
I have written a JavaScript that parses the hash part of a URL in order that I can open a tab, load content into a div and scroll to a named anchor.
I can get the tab to open and content to load fine, however, scrolling to the anchor is proving to be a real pain.
I can get it to work if I trigger an alert before the scroll to the anchor (forcing you to click 'OK', but if I disable the alert (which I want to), it doesn't work.
I am guessing that it has something to do with waiting for the functions to finish processing first? But I'm quite inexperienced with JavaScript so would appreciate any help anyone could could give.
URL example: http://www.mysite.com/abc.html#tab=tab-3&#srv=includes/xyz&#anc=myanchor
My JavaScript is as follows:
$(document).ready(function () {
var parts = location.hash;
parts = parts.split('&'); // Hash parts of URL.
for (var i = 0; i < parts.length; i++) // Loop to separate variables.
{
if (parts[i].substr(1, 4).toUpperCase() == "TAB=") {
var tab = parts[i].substr(5).split("-").pop() - 1
} // Tab no. part from tab name.
if (parts[i].substr(1, 4).toUpperCase() == "SRV=") {
var srv = parts[i].substr(5)
} // Path to content to load.
if (parts[i].substr(1, 4).toUpperCase() == "ANC=") {
var anc = parts[i].substr(5)
} // Named anchor to locate.
};
// Tab found in URL so we'll check it exists and open it.
if (tab == -1) // Tab not found?
{
tab = 0 // Default to 1st tab if not.
};
$("#tabs").tabs("option", "active", tab); // Select the tab.
// Load content if provided in URL.
var href = $('.list li a').each(function () {
var href = $(this).attr('href');
if (srv == href.substr(0, href.length - 5)) {
var toLoad = srv + '.html .maint_content';
$('.maint_content').load(toLoad)
}
});
// Load content when selected from list.
$('.list li a').click(function () {
var toLoad = $(this).attr('href') + ' .maint_content';
$('.maint_content').fadeOut('normal', loadContent);
$('#load').remove();
$('.maint_content').fadeIn('normal', loadContent);
$('#load').fadeIn('normal');
window.location.hash = $(this).attr('href').substr(0, $(this).attr('href').length - 5);
function loadContent() {
$('.maint_content').load(toLoad, '', showNewContent());
}
function showNewContent() {
$('.maint_content').show('normal', hideLoader());
}
function hideLoader() {
$('#load').fadeOut('normal');
}
return false;
});
// Scroll to locate anchor.
//alert(anc);
$('html, body').animate({
'scrollTop': $('#' + anc).offset().top
}, 1000);
});
Thanks
I'm assuming the anchor you want to jump to is in the loaded content?
If so, just add a callback to the ajax call and do the jump there:
$('.maint_content').load(toLoad,'',showNewContent()).done(function () {
window.location.hash = $(this).attr('href').substr(0,$(this).attr('href').length-5);
});
You are ajaxing the content of the div. This means it is loading asynchronously. The animate function is thus called before the loading of the content has finished, and the anchor you are looking for is not yet there.
In order to fire the animation when the loading is complete, you can pass a function to .load()
$('.maint_content').load(toLoad,'',showNewContent());
so you allready got that: showNewContent is fired when the loading finishes.
So all you have to do is move your animation line to the end of that function.
function showNewContent()
{
$('.maint_content').show('normal',hideLoader());
$('html, body').animate({'scrollTop': $('#'+anc).offset().top}, 1000);
}

JQuery doesn't handle dynamic events with .load

On a webpage, when you click a link, the content of that page is fetched using the .load function. If nothing is found, then nothing happens.
The issue now is that if you go from one page to the next, and then back again, the code doesn't work.
Its a lot easier to go to my test site - http://joshblease.co.uk/JQuery/
Click on the orange link "Page"
Click on the header link "Index"
Now click on the orange "Page" link again
The page refreshes and I do not know why?
Edit: I am using Chrome to test this at the moment
Edit: My Code
function JLoad (url, elem, type) {
if(type != "ext"){
var $link = elem;
var uri = url + ' #content';
$("#content").load(uri, {'body_only': '1'}, function(response, status, xhr){
if (status != "error") {
if (window.history && window.history.pushState){
window.history.pushState({}, 'JoshBlease Test', url);
}else{
window.location.hash='!/'+url;
}
if(type == "menu"){
$("a").removeClass("active");
$link.addClass("active");
}
}else{
if(type == "menu"){
var linkText = $link.text();
$link.fadeOut(function(){$link.addClass("notFound").text("404 Error")}).fadeIn().delay(2000).fadeOut(function(){$link.removeClass("notFound").text(linkText)}).fadeIn();
}
}
});
}
};
This is in the header of the pages:
$(document).ready(function() {
$("a").on("click", function(event) {
console.log("Click");
event.preventDefault();
var url = $(this).attr("href").replace('./', '');
JLoad(url, $(this), $(this).attr("type"));
return false;
});
});
The Link being used:
Page
I am not sure why, but by using an old version of JQuery, I replaced the "on" event with "live". It now seems to work?

Categories