Randomly failing Clone() in Snap.SVG - javascript

I'm having a problem where occasionally clone() is failing inside Element.insertAfter because el.node.parentNode is null.
Inside top level html
Snap.load(path, function (data) {
window.picto_svg = data.select('svg');
...
jQuery('#content').load('interior.html');
});
Inside interior.html
$(document).ready(function () {
var svg_copy = picto_svg.clone();
});
Am I just getting lucky that it usually doesn't fail? I don't want to display the svg before cloning. I end up cloning it and adjusting the clone several times, so just want to keep one pure one.

I would probably post more code on a jsfiddle, as there's not enough information to be sure.
However, my first guess would that you need to move this line..
var svg_copy = picto_svg.clone();
Inside the Snap.load function. The problem is possibly that you are trying to create a clone before the file has been loaded, so sometimes it works, sometimes it doesn't.
Its also not clear if you are appending the svg or not as well, but first I would make sure that all function calls and variable assignments that rely on a file being loaded are called from the load function.

Yes, it seems I was getting lucky. To reliably clone an Element, it must have been appended first. I appended to a hidden div and the random failures went away.
Snap.load(path, function (data) {
var placeholder = Snap('#placeholder');
var appended = placeholder.append(data);
window.picto_svg = placeholder.select('svg');
http://codepen.io/matelich/pen/BKyVYv

Related

Loop through an SVG file in Javascript

I'm trying to go through a rather basic SVG document with four <rects>, each with a unique ID. I want to add those elements to an array.
This is what I have...
// event listner to make sure the page has loaded before running the scrupt
window.addEventListener("load", function(){
// gets an SVG object from the inline HTML
var svgObject = document.getElementById('rectTestSvg').contentDocument;
var elementList = [];
for(var i = 0; i < svgObject.numElements; i++){
if(svgObject[i].id('*_rect') === true)
{
elementList.push(svgObject.getElementAt(i));
}
}
console.log(elementList);
});
It doesn't work past getting the svgObject, but hopefully it at least helps illustrate the idea.
Any help anyone could throw my way would be really appreciated
In HTML5, SVG elements and their content are "just DOM elements", so queryselect the elements directly, and then form an array directly off of the result:
var myRects = Array.from(svgObject.querySelectorAll("rect"));
Done.
And the reason you want Array.from is because query selections are NodeList objects - while they're static (in this particular case, but most definitely not always so read the function documentation for functions you use!) they do not have any of the array functions that makes working with lists actually easy (map, filter, etc) so we turn it into an Array for writing normal code.
Do note that if your SVG object lives inside an iframe, you're almost guaranteed to not "just have access" to it, in which case you'll need to make sure the SVG document loads its own script that can talk to the parent page through a window.postMessage() channel (or websocket, but that's overkill).

Error using built version of Dojo (but not the uncompressed source)

I noticed something weird when using the uncompressed source of Dojo our code runs normally without error. I tried these two from the archives so far
dojo-release-1.10.6-src and dojo-release-1.10.8-src
However when I switch to the built versions, either
dojo-release-1.10.6 or dojo-release-1.10.8
There is an error that occurs when using dojo.query
TypeError: root.getElementsByTagName is not a function
My function call looks like this
var dom_frag = domConstruct.toDom(response);
var title = dojo.query(".accordion_title", dom_frag)[0];
where response contains HTML string. (too long to post here)
EDIT: Image of debugger showing contents of 'dom_frag'
Ok, have you checked to see if the dom_frag variable is a single dom node? If the dom fragment is multiple nodes, then the dojo.query won't work, because it needs to search the children of a single dom node.
To solve this, try wrapping the toDom contents with a single node... like so:
var dom_frag = domConstruct.toDom("<div>"+response+"</div>");
var title = dojo.query(".accordion_title", dom_frag)[0];
This is, of course, a bit of a hack... but if you can't guarantee that the response will end up a single node, then you need to do it.
Make sure your root is actually a DOM element as:
the Element.getElementsByTagName() method returns a live
HTMLCollection of elements with the given tag name. The subtree
underneath the specified element is searched, excluding the element
itself. Ref.

Closures in For Loops With Onclick Assignment

I'm trying to have a navBar that generates automatically by looping through an array of "Page" objects. Unfortunately, I seem to be falling into the loops/closure trap. I have read several threads related to this and in some cases have copy and pasted solution code and passed in my own variables but I'm struggling to make it assign onclicks correctly.
I know I'm close. In the below code are two options that I have tried.
Am I getting something wrong with the paremeter in parenthesis in the self-calling function? - the ()(divId)? I don't really understand this part.
Could I also be struggling because this is being done as an object method?
Any help much appreciated but go easy on me, I'm learning all this in my spare time! ;)
Thanks in advance.
EDIT: Jsfiddle: https://jsfiddle.net/mcgettrm/fs0mtz6n/
var navBar = {
display: function(){
for(i=0;i<pages.length;i++){
document.getElementById('navBar').innerHTML += pages[i].token;
var divId = pages[i].unique;
// code works fine up to here.
// option one(below): when navBar.display() is called the following code only adds
// the onclick to the final navbar link
document.getElementById(divId).onclick=(function(divId) {
return function() {
alert(divId);
};
})(divId);
//option two(below): when navBar.display() is called the following code logs
// the individual div id's correctly. But, it does it without being clicked. Then,
// only the last item in the loop is clickable.
(function(divId){
document.getElementById(divId).onclick= function(){
console.log(divId);
}
}
)(divId);
}
}
};
I've got it working here - https://jsfiddle.net/pqu9kr85/ it doesn't seem to have been to do with the binding of i more that you needed to build up the navigation html first, making sure it was in the DOM before binding the event. I put two separate loops, one to generate the nav, the second to bind the events. Also updated the page.display() to use this as that will have been affected by the value of i.

Why can't I position the browsers focus on the svg/div element that I need?

Okay, so this is annoying the hell out of me, I have a webpage with a whole bunch of svgs on it (the svg JQuery plugin), I needed the browser to focus on certain ones(horizontally), so I made a simple function to achieve my end:
function adjustWindowPos(svg){
var left = svg.scrollLeft();
$(document).scrollLeft(left);
}
Where the svg object is just an object we get via (we draw and shit to is before we do this, so it is rendered):
$("#someDivID").svg('get');
I've tested this last night and everything worked fine, this morning I come into work and it throws an error in firebug saying:
TypeError: svg.scrollLeft is not a function
I realize that it can't find the function because the object is an SVG wrapper not a JQuery object per say...but it worked yesterday. That's what I don't get (a lot of things, no coffee today).
You need to create jQuery-object that will contain svg DOM-element, that is jQuery wrapper which you are looking for. You can do it several ways using the $ jQuery function:
pass a jQuery selector string to it: $('jquery selector');
pass a DOM object to it;
get it from already existing jQuery object, if you have one;
Let me suppose that you have an id-selector:
var svg = '#id_of_the_svg_element'
function adjustWindowPos(svg){
var $svg = $(svg); // now this is the jQuery object
var left = $svg.offset().left; // this is its horizontal position relative to the document
$(document).scrollLeft(left);
}

Populate multiple fields based on select value from JSON

I am trying to populate multiple form fields with JSON data after a user makes a choice from a select box. I am very new to jquery so sorry if I am getting something elementary wrong. I don't want to include the JSON in my html because it will be changing often and it is a very large file. Here is what I have:
<script type="text/javascript">
$.ajax({
url: '../includes/json/data/abbc.json',
success: function(data) {
$("#rig").html("<option >--select--</option>");
$.each(rigdetaillist.rigs,function(){
var rigName=this.rig;
$("#rig").append("<option value=" +rigName + ">" +rigName + "</option>");
});
$("#rig").change(function(){
var rigValue=$(this).val();
$.each(rigdetaillist.rigs,function(i){
var rigName=this.rig;
if (rigName==rigValue){
$(".rigdetail").val("");
$.each(rigdetaillist.rigs[i].rigdteails,function(i){
var rigdetailName=this.rigdetail
$(".rigdetail").eq(i).val(rigdetailName);
});
}
});
});
}
});
</script>
There are a few things here that you want to consider, one of the major things being that you want to do as little DOM manipulation as possible, as its one of the major causes of performance issues.
Unfortunately, I don't have the time to rewrite your code and give you what you should write. Although I do have the time to explain to you what you need to consider and hopefully it'll lead you down the right path :-)
(1) Your data var in success: function (data) { isn't being used. I'm assuming you mean it to be used for the rigdetaillist, most likely something like var rigdetaillist = data['rigdetaillist']; depending on your JSON. In either case, data is your json return value, of which you aren't referencing at all...which you probably need to. :-)
(2) As I said earlier, you want to do as little DOM manipulation as possible. So you probably will want to either pull out the #rig and cache it in javascript (to be put back into the DOM later), or create a new $(<script>) obj and then copy its html into $('#rig')'s when you are done. I would suggest creating the new script tag as detaching is a very nice feature of jquery, but can have its problems (such as when you go to append it back in, it appends at the bottom of the container instead of where you originally had it).
(3) The change function is being created on every json request. There is more than likely a way to globalize that so you don't have to create a new change function every time. You could possibly store the necessary information in a global variable and just reference that variable in change function, or I'm sure you could do some other really cool scope tricks, but that would be the simplist.

Categories