Best practice for handling jquery ajax response - javascript

I've done a lot of reading over the last few years trying to become proficient with JavaScript and it's frameworks. It seems that most texts either teach the rudiments of ajax, loading something into an empty element, or they assume you know what your doing and glance over the data handling portion. I've developed my own methods over time, but having nothing to compare them too. I don't know how efficient they are. When building a ajax based site I typically use jquery and history.js and my code is as follows.
function updateContent(state){
var theUrl = state.data.urlPath; //the url of what needs ajaed in
$('#container #content').fadeOut(500, function(){ //fade out old content
var newContent = {};
newContent.load(theUrl + ' #content', function(){ //load new content into object
newContent.hide(); //hide it so it can be faded in
$('#container').html(newContent); //replace content in container
newContent.fadeIn(500); //fadein content
});
});
}
var History = window.History;
if (History.enabled) {
var State = History.getState();
History.pushState({urlPath: window.location.href}, $("title").text(), State.urlPath);
} else {
return false;
}
History.Adapter.bind(window, 'statechange', function() {
updateContent(History.getState());
});
$(document).on('click', 'a', function(evt){
var testSite = /http:\/\/www.example.com(\/)?/ //regex to test if is local link
var theUrl = $(this).attr('href'); //the url of the clicked link
if(testSite.test(theUrl)){
evt.preventDefault();
History.pushState({urlPath: theUrl}, title, theUrl);
}
});
This is the basics of my code. I feel it is inefficient particularly when the back button is used. When going back the content has to be reloaded. Is there a more efficient way to do this?

Related

changing the url without loading a new page

For my single page website, I have an index of projects. If a project is clicked it opens up a slideshow on the same page. So basically, I only have one URL, and if anyone wants to link to a specific project, they can't.
I'm trying to make it so that if I click on a project, it changes the URL. And so that URL can be used to get to my website with that project opened.
Here is a link to what I have so far.
For reference, I'm trying to achieve something that is found on this site.
I found some good suggestions here, but what happens when I use something like this (below), a new URL is created but it doesn't open up the project if I renter that URL into the browser.
<a href="#" id='click'>Click to change url to bar.html</a>
<script type="text/javascript">
var stateObj = { foo: "bar" };
function change_my_url()
{
history.pushState(stateObj, "page 2", "bar.html");
}
var link = document.getElementById('click');
link.addEventListener('click', change_my_url, false);
</script>
function processAjaxData(response, urlPath){
document.getElementById("content").innerHTML = response.html;
document.title = response.pageTitle;
window.history.pushState({"html":response.html,"pageTitle":response.pageTitle},"", urlPath);
}
You can use `window.onpopstate to sense the back/forward button navigation
window.onpopstate = function(e){
if(e.state){
document.getElementById("content").innerHTML = e.state.html;
document.title = e.state.pageTitle;
}
};
I would appreciate someone with more skill to check this over for me
You can use id at elements which has slideshow as unique URL; at .ready() start animation of element where id matches .location.hash
$().ready(function() {
// `location.hash`: `id`: `#slideshow1` of element linked to
// from, e.g., `http://example.com/#slideshow1`
var currentSlideshow = $(location.hash);
// do slideshow stuff at `currentSlideshow`: `#slideshow1` element
})
using a hash might work best in this case
$(document).ready(function({
//loading a page
var project = window.location.hash
yourProjectLoadFunction(project);
//setting a url
$('.number').click(function(e){
$this = $(this);
window.location.hash = $this.attr('id');
});
});

Refresh Button Update Accordion in Javascript

I would like to edit accordion header (formationName) and once I click on the refresh button, it should update the accordion header. I couldn't figure out how to approach the problem.
$("#refresh").click(function(){
myData.offsetFormations[0]["FormationName"]="party";
json = JSON.stringify(myData);
alert( json );
});
fiddle: http://jsfiddle.net/xg7cr0g4/76/
It depends on your scenario. If there is a ton of data being refreshed often, you would want to do an in place edit.
If you just refresh when clicked and the data is insubstantial, just rebuild the table the way you originally did. An example of this (although it's not quite working):
http://jsfiddle.net/xg7cr0g4/78/
var build = function(){
//...build the grid/accordion here on demand (load,reload,programmatically)
};
var refresh = function(){
//update json
build(); //rebuild;
};
$(document).ready(function(){
// build on load
build();
});
jQuery UI accordion needs to be destroyed on rebuild:
re-initialize jquery accordion on callback
Here is the working fiddle:
http://jsfiddle.net/xg7cr0g4/79/
You can also organize your code better and avoid global functions by following the module pattern:
var Grid = function(){
var self = this;
this.build = function(){
...
};
this.rebuild = function(){
...
};
this.init = function(){
...
this.build();
$('#refresh').on('click', this.refresh);
...etc
};
$(this.init);
};
var grid = new Grid();
grid.rebuild();
To answer your other question, just add an additional method:
this.setHeader = function(header){
// similar to rebuild, change the json using param
x[...] = header;
this.build();
};

How do I make my click catcher work?

I'm trying to create a simple click catcher where if you click .image-class the javascript will take the href from another element with a class name of .btn and send you to it's destination. Though I keep getting errors on lines 7 & 10 saying that undefined is not a function. How do I make this work?
<script>
var ClickCatcher=
{
init:function(){
var link = jQuery('.btn')[1].href;
var imgCatch = jQuery('.image-class');
imgCatch.addEventListener("click", ClickCatcher.clickListener, false);
},
clickListener:function(){
window.location = link;
}
};
ClickCatcher.init();
</script>
You can do this with jquery with a simple click event
jQuery('.image-class').on('click', function (){
window.location = jQuery('.btn').eq(1).attr('href');
});
But if you still want to write in the way you have you can do:
var ClickCatcher = {
init: function () {
jQuery('.image-class').on('click', function (){
window.location = jQuery('.btn').eq(1).attr('href');
});
}
};
ClickCatcher.init();
Just make sure to fire the init method after dom load.
update: One issue with it is that you have coded your target etc in the code rather then pass it, so its going to be hard to reuse, you'd be better off doing:
var ClickCatcher = {
init: function ($button, loc) {
$button.on('click', function (){
window.location = loc;
});
}
};
ClickCatcher.init(jQuery('.image-class'), jQuery('.btn').eq(1).attr('href'));
That way the internal working is seperate from the dom (as you are passing the dom dependencies to the function.
#atmd showed a very good way of doing this. If you just want to know what your mistake was though. It is wa an error in your jQuery stament to get the btn href
jQuery('.btn')[1].href
you need to call the attr function and then get the href attr. and use .eq(1) to reduce the set to the first btn
jQuery('.btn').eq(1).attr('href);

Targetting elements loaded with jquery load()

I am doing a website where all internal links make the current page fade out and the new contents fade in. I do that with jquery load(). The loading and fading part works fine like this:
var $mainContent = $("#ajaxcontainer"),
$internalLinks = $(".internal"),
URL = '',
$ajaxSpinner = $("#loader"),
$el;
$internalLinks.each(function() {
$(this).attr("href", "#" + this.pathname);
}).on('click', function() {
$el = $(this);
URL = $el.attr("href").substring(1);
URL = URL + " #container";
$mainContent.fadeOut(500, function() {
$ajaxSpinner.fadeIn();
$mainContent.load(URL, function() {
$ajaxSpinner.fadeOut( function() {
$mainContent.fadeIn(1000);
});
});
});
});
As you can see, I am targetting all internal links by a class I've given them (.internal). My problem is that once content gets loaded with ajax, I am not able to target this new content with my jquery, and so the $internalLinks.each() and so on gets broken, meaning that the site just reverts back to the default link behavior.
Another thing which is related to this, is that I want to be able to target this newly loaded content with the jquery.masonry plugin. That also isn't possible the way I'm doing things now.
Thank you very much.
When you update the page, the old .internal links are removed, so the event handler attached to them won't work. Change your code to use event delegation:
$('.internal').each(function() {
$(this).attr("href", "#" + this.pathname);
});
$(document).on('click', '.internal', function() {
$el = $(this);
URL = $el.attr("href").substring(1);
URL = URL + " #container";
$mainContent.fadeOut(500, function() {
$ajaxSpinner.fadeIn();
$mainContent.load(URL, function() {
$ajaxSpinner.fadeOut( function() {
$mainContent.fadeIn(1000);
});
$('.internal').each(function() {
$(this).attr("href", "#" + this.pathname);
});
});
});
});
As you see, I refresh the attribute href of each link after a refresh, too.
** EDITED ** I was missing changing the href attribute the first time. Now it should work!

How to retain Javascript state on page change

I have an accordion style navigation list set up so that when categories are clicked it opens up to show sub-categories that link to pages.
What I would like to do is have the accordion navigation list keep it's open or closed state when the new page opens.
I've gathered that cookies work to retain the state on refresh, but how do I retain the state when a different page is visited? All the pages have the same accordion navigation list.
Try Web Storage. Store the state of the tabs on page unload, restore the state on the page load event.
I found a solution, it uses the accordian plug-in found here, http://www.i-marco.nl/weblog/archive/2010/02/27/yup_yet_another_jquery_accordi and the jquery cookie.js plug-in
I added id's to the header anchor tages in the HTNL mark-up like so,
<li>
<a id="m1" class="label" href="#">Sound/Audio Systems</a>
<ul class="acitem">
<li>PA Systems</li>
<li>Loudspeakers</li>
<li>Microphones </li>
<li>DJ Equipment</li>
<li>Sound Processing Equipment</li>
</ul>
</li>
And modified the accordian.js code, I added the lines beginning with $.cookie, and the If statement in the document.ready funciton.
jQuery.fn.initMenu = function() {
return this.each(function(){
var theMenu = $(this).get(0);
$('.acitem', this).hide();
$('li.expand > .acitem', this).show();
$('li.expand > .acitem', this).prev().addClass('active'),
currentID = "";
$('li a', this).click(
function(e) {
e.stopImmediatePropagation();
var theElement = $(this).next();
var parent = this.parentNode.parentNode;
if($(parent).hasClass('noaccordion')) {
if(theElement[0] === undefined) {
window.location.href = this.href;
}
$(theElement).slideToggle('normal', function() {
if ($(this).is(':visible')) {
$(this).prev().addClass('active');
currentID = $(this).prev().attr('id');
$.cookie('menustate', currentID, {expires: 2, path: '/'});
}
else {
$(this).prev().removeClass('active');
$.cookie('menustate', null, {expires: 2, path: '/'});
}
});
return false;
}
else {
if(theElement.hasClass('acitem') && theElement.is(':visible')) {
if($(parent).hasClass('collapsible')) {
$('.acitem:visible', parent).first().slideUp('normal',
function() {
$(this).prev().removeClass('active');
$.cookie('menustate', null, {expires: 2, path: '/'});
}
);
return false;
}
return false;
}
if(theElement.hasClass('acitem') && !theElement.is(':visible')) {
$('.acitem:visible', parent).first().slideUp('normal', function() {
$(this).prev().removeClass('active');
$.cookie('menustate', null, {expires: 2, path: '/'});
});
theElement.slideDown('normal', function() {
$(this).prev().addClass('active');
currentID = $(this).prev().attr('id');
$.cookie('menustate', currentID, {expires: 2, path: '/'});
});
return false;
}
}
}
);
});
};
$(document).ready(function() {
$('.menu').initMenu();$('#side-navigation_frame').show();
if ($.cookie('menustate')) {
var anchor = "",
elementID = $.cookie('menustate');
anchor = document.getElementById(elementID);
$(anchor).addClass('active');
$(anchor).next().show();
}
});
It works nicely, not bad for a beginner, thanks for all the advise.
Rob Fenwick
Cookies "retain state" across the full path and domain for which they are specified. So if you can get them to work for just one page, you should have them work automatically on all pages of your site.
You can still use cookies, you just have to make sure they're not specific to the one page. For example:
document.cookie = 'openitem=5; expires=somedate; path=/';
will be accessible to all pages on the site. More about cookies.
Ok so I took a look at the library you are using, it's a decent library and all but you might find it easier to find solutions to your problems if you use a more standard library like jQuery UI, it has an accordion control http://jqueryui.com/demos/accordion/ and like I mentioned there are so many people using it that the answer to most problems can be found.
But like I mentioned I did take a look at your library. As others have mentioned you would use a cookie to store the value. This library supports 'pre expanding' a particular section of the accordian, to do that you would add the expand class to the element. You can either do that server side or you can do it using JavaScript before initMenu() is called.
The other less elegant option is to trigger the click event on the anchor tag after the call to initMenu. Finally you can use jQuery's show() to show expand the section without animation.
The first thing you have to do is find out which section was clicked on, then you would store that sections name in a cookie. On page load you would get that value and expand the appropriate according section. This is what the code should kinda look like - note this is psuedo code and you have fill in the appropriate parts.
$(function() {
$(".menu.collapsible .label").click(function() {
var accordianSection = $(this).text();
rememberSection(accordianSection);
});
var section = recallSection();
if(section !== undefined) {
expandSection(section);
}
});
The expandSection function can look something like this:
var sectionLink = $(".menu.collapsible .label").filter(function() {
return $(this).text() == section;
});
sectionLink.trigger('click');

Categories