Reputation: 186
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?
Upvotes: 0
Views: 1416
Reputation: 186
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!
Upvotes: 0
Reputation: 183
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.
};
Upvotes: 0
Reputation: 3767
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'));
}
},
...
Upvotes: 1