Why is this Jquery / JavasScript so slow? - javascript

I have this script, and there is a very noticeable 1-2 second delay to change the border color around a textbox (has-success) or (has-error). Basically I want to change the color (Green or Red) and show/hide a glyphicon based on an if/else statement.
$(document).ready(function () {
$("#lookupExtGuest").click(function () {
$.ajax({
url: "/NewUserRequest/LookupData",
data: { userInput: document.getElementById("ExtGuestID").value },
success: function (result) {
if (result.indexOf("was found") != -1) {
var d = document.getElementById("extGuestlookup")
d.className = d.className + " has-success"
$('#extGuestGlyphiconOK').show();
$('#extGuestGlyphiconRemove').hide();
}
else {
var d = document.getElementById("extGuestlookup")
d.className = d.className + " has-error"
$('#extGuestGlyphiconOK').hide();
$('#extGuestGlyphiconRemove').show();
}
}
});
});
});
Here is the reponse times from the chrome Network menu:

The javascript in the success function should only take a few milliseconds to run provided the result string is only a few kilobytes. A good way to test something like this is with console.time():
<script>
$(document).ready(function () {
$("#lookupExtGuest").click(function () {
console.time('lookupDataRequestTimer');
$.ajax({
url: "/NewUserRequest/LookupData",
data: { userInput: document.getElementById("ExtGuestID").value },
success: function (result) {
console.timeEnd('lookupDataRequestTimer');
console.time('lookupDataCallbackTimer');
if (result.indexOf("was found") != -1) {
var d = document.getElementById("extGuestlookup")
d.className = d.className + " has-success"
$('#extGuestGlyphiconOK').show();
$('#extGuestGlyphiconRemove').hide();
}
else {
var d = document.getElementById("extGuestlookup")
d.className = d.className + " has-error"
$('#extGuestGlyphiconOK').hide();
$('#extGuestGlyphiconRemove').show();
}
console.timeEnd('lookupDataCallbackTimer');
}
});
});
});
</script>
I've added some console.time functions into the code you posted. If you run this you should see the timing in the web-inspector's console. This way you can see whether the ajax call (lookupDataRequestTimer) or the success callback (lookupDataCallbackTimer) is slower and by how much.
If the ajax call is much slower and the file being requested isn't too large (which I suspect) you'll probably find the server is quite slow. To speed things up you could run the GET request on page load and cache the data into a variable and access it immediately on click.
Edit: I see you've just added the network screenshot. The request's size is very small, 590b, but it's taking 2.47s. This is definitely the server which is taking a while to respond. Can you take another picture of the entire network tab, including the times for the html page itself.

Maybe you can use ('#parent children').remove() and then ('#parent').append('<element><\element>') for each node.
Or, to hide slow:
hide('slow') or show('slow')

Related

Getting the id of an asynchronously loaded svg element with Jquery

After several hours of fruitless experimentation, web searches, consultation of the Jquery API docs and poring over seemingly related stackoverflow posts, I've decided my only hope to getting this to work is to turn to the community here. Hoping someone can give me some insight into why this isn't working and how to fix it!
Within the web app I'm developing, I use several interactive SVG images, sometimes with multiple instances on a single page. In order to prevent distinct SVG images from stepping on each others' toes, interactive elements within each all carry a "barcode" of sorts, appended to their id attribute. In turn, the barcode for each image is encoded in the id attribute for the svg tag. In order for the page to display the svg with the correct initial state, I need to get the id attribute from the svg immediately after loading and pass it to another script to set the correct attributes within the target SVG.
Individual SVG's are loaded via an AJAX call, thusly:
$(document).ready(function(){
//mapId is supplied in various ways in different contexts, for example:
var mapId = $("#mapId").name;
loadSvgMap(mapId);
}
function loadSvgMap(mapId) {
return jQuery.ajax({
url: "[% c.uri_for('/maps/update_map/') %]" + mapId,
success: function(result) {
if(result.isOk == false) {
alert("Bad response from server: " + result.message);
} else {
$("#som_map").load(result);
}
},
error: function(result, status, errorThrown) {
alert("Map retrieval failed: " + status + " " + result.status);
},
async: true,
dataType: 'text',
cache: false
});
}
At this stage, I need to get the id attribute of the SVG that was loaded. The problem is, the SVG does not seem to exist on the page when I try to do so:
$(window).load(function() {
var svg = $("svg")[0];
console.log(svg);
//returns "undefined"
}
The issue seems to be that the ajax call has not completed when the next block of code executes, so the SVG is not yet present in the DOM. I've tried using $.when within $(document).ready), and this is the method I would like to use, but it still doesn't seem to wait until the SVG is loaded:
$.when(loadSvgMap(mapId)).done(function(a) {
var map = $("svg")[0];
console.log(map);
//undefined
});
I have figured out a workaround, but it is not ideal because the request fires after every ajax request, not just those that change the SVG...
$(document).ajaxStop(function() {
var svg = $("svg")[0];
if (svg != null) {
var map = svg.id;
console.log(map);
// do other stuff
}
});
Having this fire after every ajax request does not break anything at the moment but if I can get the $.when method working properly, it seems that should be less likely to break things down the road. Any ideas?
When working with asynchronous functions the best place to put code like this is in the callback function—Which runs as soon as control returns from the initial asynchronous request. In this case it would be in the success attribute of your jQuery.ajax(.. call:
function loadSvgMap(mapId) {
jQuery.ajax({
url: "[% c.uri_for('/maps/update_map/') %]" + mapId,
success: function(result) {
if(result.isOk == false) {
alert("Bad response from server: " + result.message);
} else {
$("#som_map").load(result);
// grab id here
console.log($('svg').attr('id'));
}
},
...
Id's and class names in SVG are objects rather than strings of HTML so the id and class selector used by jQuery won't work. IE..jQuery only understands the HTML DOM and not the SVG DOM.
So, given something like:
<object id="svg" data="img/yoursvg.svg" type="image/svg+xml" height="50" width="50">
</object>
You can use something like this:
window.onload=function() {
// The Object
var a = document.getElementById("svg");
// The SVG document inside the Object tag
var svgDoc = a.contentDocument;
// One of the SVG items by ID;
var svgItem = svgDoc.getElementById("svgItem");
// Do something to it.
};
So the problem turns out to be the call to .load() nested within the original ajax function. The outer call just retrieves the location of a static svg file, which is then loaded into the page with the inner ajax call. Of course, .when() only knew about the outer call, so it was firing before the svg had actually loaded.
The solution was to have a separate ajax function for the "inner" call, using .html() to load the content, and call it from a nested .when():
function loadSvgMap(mapId) {
return jQuery.ajax({
url: "[% c.uri_for('/maps/update_map/') %]" + mapId,
success: function(result) {
if(result.isOk == false) {
alert("Bad response from server: " + result.message);
} else {
// Simply return the result
}
},
error: function(result, status, errorThrown) {
alert("Map retrieval failed: " + status + " " + result.status);
},
async: true,
dataType: 'text',
cache: false
});
}
function loadSvgContent(mapUrl) {
return jQuery.ajax({
url: mapUrl,
success: function(result) {
if(result.isOk == false) {
alert("Bad response from server: " + result.message);
} else {
$("#som_map").html(result);
}
},
error: function(result, status, errorThrown) {
alert("Map retrieval failed: " + status + " " + result.status);
},
async: true,
dataType: 'text',
cache: false
});
}
$.when(loadSvgMap(mapId)).done(function(mapUrl) {
$.when(loadSvgContent(mapUrl)).done(function(a) {
var svg = $("svg")[0];
var map = svg.id;
console.log(map);
// Works!!
});
});
Many thanks to #MikeMcCaughan and #ThomasW for pointing me in the right direction!

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.

Execute code after page is fully loaded using JavaScript

I tried for several hours to get the following code working.
The code should be paused until the page is loaded. The problem that I'm encountering is that the AJAX is executing asynchronously. How would I force the code to wait before executing?
var i = 1;
function On_Success(){
i = i+1;
if(i<=10){
$('<div id="p'+i+'">b</div>').replaceAll('#b');
$('#p'+i).load('tabelle.html');
//here shoul be waited, til the page is loaded
On_Success();
};
};
function knopf(){
$('body').append('<div id="p'+i+'">a</div>');
$('#p'+i).load('tabelle.html');
On_Success();
};
Both Ajax and load have an on successs function that can be run when the response is fully returned.
$.ajax({
async: true,
type: "GET",
url: url,
data: params,
success: function(html){
//do stuff here
},
error: handleAjaxErrors
});
If you want to use load, there is a success callback as well:
$("#myDiv").load('/ajax/data/get', function() {
//do stuff here
});
The load function has a success handler:
$('#p'+i).load('tabelle.html', function() {
On_Success();
});
The success handler is only called after the ajax call completes successfully, and after the provided content has been added to the DOM.
You can see full documentation here:
http://api.jquery.com/load/
If you also want to capture error conditions on the client you will need to use the full $.ajax call as per #Chris's answer.
.load accepts a callback that runs when loading is complete; you can use this instead of the more verbose jQuery.ajax function, which will require you to write additional code to populate your element.
jQuery("#p", i).load("tabelle.html", function() {
//code to run when tabelle.html has finished loading
});
Change your code to the following. It should work:
var i = 1;
function On_Success() {
i = i + 1;
if (i <= 10) {
$('<div id="p' + i + '">b</div>').replaceAll('#b');
$('#p' + i).load('tabelle.html', On_Success);
};
};
function knopf() {
$('body').append('<div id="p' + i + '">a</div>');
$('#p' + i).load('tabelle.html', On_Success);
};
On another note, is it absolutely necessary that you should wait for one load to complete before populating the other divs? Why can't you do something like this:
function On_Success() {
var i = 0;
while(i < 11) {
$('<div id="p' + i + '">b</div>').replaceAll('#b');
$('#p' + i).load('tabelle.html');
};
};
Also, I'm not sure what the knopf function is doing. What should the value of i be in that function? How are you calling your code?

Why isn't my callback being called on one page only?

I am using jQuery to grab some JSON and then plug it into some elements and display it on my page.
It works fine on all pages except one, where the response seems to be the page itself.
I have placed alert()s in the callbacks (success and complete) and they never seem to be fired (though Firebug shows the request returning 200 OK which should trigger the success handler).
I don't know what to do, I've never encountered this before.
Here is the jQuery code I am using:
var specials = (function() {
var specials = false,
specialsAnchor;
var init = function() {
specialsAnchor = $('#menu-specials a');
specialsAnchor.click(function(event) {
event.preventDefault();
if (specials != false && specials.is(':visible')) {
hide();
} else {
show();
}
});
};
var load = function(callback) {
specialsAnchor.addClass('loading');
specials = $('<div />', { 'id': 'specials' }).hide().appendTo('#header');
var specialsUl = $('<ul />').appendTo(specials);
$.ajax(specialsAnchor.attr('href'), {
dataType: 'json',
success: function(data) {
$.each(data, function(i, special) {
specialsUl.append('<li><h4>' + special.heading + '</h4><p>' + special.content + '</p></li>');
});
specialsAnchor.removeClass('loading');
callback.call();
}
});
}
var show = function() {
if (specials == false) {
load(show);
return;
}
specials.slideDown(500);
}
var hide = function() {
specials.slideUp(500);
}
$(init);
})();
What is going on?
I noticed that you're including jquery.validate on this page, but not the others. jQuery validate with jQuery > 1.5 causes some issues with AJAX calls.
I realize the linked question/answer aren't exactly what you're seeing, but I've seen all kinds of weird issues with AJAX calls and this combination of validate and jQuery, so I figured it would be worth mentioning.
Hope that helps.
This is probably not a complete answer, but could be a step in the right direction. Using Charles Proxy it seems on your other pages when I click specials, it makes a request to http://www.toberua.com/~new/specials however on the contact-us page the ajax request is instead going to http://www.toberua.com/~new/contact-us (which of course is not json)
One other interesting note:
The XMLHttpRequest on other pages sets the Accept header properly (i.e. Accept application/json, text/javascript, */*; q=0.01 , whereas on the contact-us page it is set to Accept */*). I'd bet there's a different code branch being invoked...

Function scope within IF Statement

hoping some one can shed some light on my problem. Basicly I only want to execute a block of code if a certain DOM element exists. If it does I then perform a few bits and bobs and then call a function. However it complains that the function is not defined, suggesting that the function is not in scope. Below is the code :
$(document).ready(function ()
{
if ((document.getElementById("view<portlet:namespace/>:editSplash")!= null)) {
console.log("notifications scripted started");
// hide loading box/ notify on body load
$('.ajaxErrorBox').hide();
$('.loadingNotifications').hide();
$('.notifyWindow').hide();
getFeed();
// set up refresh button for reloading feed
$('.refreshFeed').click(function() {
$('.notifyWindow').hide();
$('.notifyWindow').empty();
console.log("notifications clicked");
getFeed();
});
// begin ajax call using jquery ajax object
function getFeed ()
{
$('.notifyWindow').empty();
console.log("ajax call for feed starting");
$.ajax ({
type: "GET",
url: "http://cw-pdevprt-05.tm-gnet.com:10040/notificationsweb/feed?username=uid=<%# taglib uri="/WEB-INF/tld/engine.tld" prefix="wps" %><wps:user attribute="uid"/>",
dataType: "text/xml",
timeout: 10000,
success: parseXml
});
};
// show loading box on start of ajax call
$('.notifyWindow').ajaxStart(function() {
$('.refreshFeed').hide("fast");
$('.notifyWindow').hide();
$('.ajaxErrorBox').hide();
$('.loadingNotifications').show("fast");
});
// hide loading box after ajax call has stopped
$('.notifyWindow').ajaxStop(function() {
$('.loadingNotifications').hide("slow");
$('.refreshFeed').show("fast");
});
$('.notifyWindow').ajaxError(function() {
$('.loadingNotifications').hide("slow");
$('.ajaxErrorBox').show("fast");
$('.refreshFeed').show("fast");
});
// parse the feed/ xml file and append results to notifications div
function parseXml (xml) {
console.log("xml parsing begining");
if (jQuery.browser.msie)
{
var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.loadXML(xml);
xml = xmlDoc;
}
$(xml).find("entry").each(function()
{
var $item = $(this);
var title = $item.find("title").text();
var linkN = $item.find("link").attr("href");
var output = "<a href='" + linkN + "' target='_self'>" + title + "</a><br />";
$(".notifyWindow").append($(output)).show();
});
}
}
else {
console.log("notifications not available");
return false;
}
});
If the DOM element exists I then try and call the getFeed function "getFeed();" however it comes back undefined. If anyone could shed some light on this it would be greatly appreciated.
Thanks in advance
It seems that you're calling getFeed before it is defined. Try moving the if statement to after the function definition. Note that this behaviour is actually implementation specific, so some browsers may work this way and some may not.
Oh - And seriously? view<portlet:namespace/>:editSplash for an id?
Problem solved - I moved my functions outside of the if statement. We live and learn lol :-)

Categories