change attributes of SVG object before appending it to DOM - javascript

Context:
I'm working on an app where I add elements dynamically to a container via myModule.getElement() . That will return me a template:
function getElement(){
var source = $("#entry-template").html();
var template = Handlebars.compile(source);
return template;
}
and then I'll just append it to a container.
The template contains an SVG object.
I'd like though to 'initialize' the element, before appending it to the DOM (or right after doing so), for example:
$(template).find("path, polygon, circle").attr("fill", "#ddd");
But that doesn't work, and my guess is because at that point the element hasn't been added to the DOM (please correct me if I'm wrong).
I can do this:
for (var i = numElements; i > 0; i--) {
elArray.push(myModule.getElement());
container.append(elArray[i]);
};
myModule.initializeElements();
and then in MyModule::
function initElements(){
$('.elClass').find("path, polygon, circle").attr("fill", "#ddd");
}
But it doesn't feel good. I wonder if I could change the properties of the SVG object before it's been appended to the DOM, is that possible?
I read that I could extend the append function and create a trigger for an event handler to it, but that seems overly complex to this situation.
Any ideas? I'm also not tied to this pattern/architecture, this is a very simple case and I could easily re-arrange it.

Related

Load SVG into a specific div with Snap SVG

What is the correct way to load an SVG file into a specific div using SnapSVG?
Following the documentation I have this JS:
var s = Snap();
Snap.load("fox.svg", function (f) {
s.append(f.select("g#fox"));
});
This loads the SVG just above the body tag, however if I try to set it's location, nothing happens, there is no error. This is what I have attempted so far:
var s = Snap('#myDiv');
Where am I going wrong?
This should work, its not far removed from your example, so its hard to tell whats wrong with yours without a live example and the svg to look at.
If you want to upload a fiddle or something, it may help.
var s = Snap("#svgdiv");
var l = Snap.load("path.svg", onSVGLoaded ) ;
function onSVGLoaded( data ){
s.append( data );
}
Edit: Just to extend on Duopixels answer, the element you are trying to add, should be an svg element (ie
<svg id="mysvgtoload">...</svg> // you can add an svg to a div
<g id="mygrouptoload">...</g> // you can't add this to a div, but could to an svg element
in the file) or add the element (g or path or whatever) to an existing svg tag/element in your html. I suspect you may be trying to add a element direct to a div, which won't work, but its hard to tell without the file.
Also double check that Snap is loaded fine, and you can do a console.log( data ) in the function to check that it has loaded the markup correct.

Removing an SVG element from the DOM using jQuery

I'm using jQuery to add an element to an embedded SVG like this:
var rect = SVG('rect');
$(rect).attr( { x: left,
y: top,
width: right - left,
height: bottom - top,
style: style } );
$(parentElement).append(rect);
parentElement could be for example $('g:first', svgRoot), where svgRoot refers to the embedded SVG element.
function SVG(elementName) {
return document.createElementNS('http://www.w3.org/2000/svg', elementName);
}
This works well, the new rectangle is shown in the browser and added to the DOM:
However, removing this rectangle fails. It is still shown in the browser and present in the DOM:
$(rect).remove();
I also tried
rect.parentNode.removeChild(rect);
which results in the error message "Uncaught TypeError: Cannot call method 'removeChild' of null".
Do you have any idea how I can fix that?
Using jQuery SVG or another plugin/framework is not possible in my project.
I ended up solving this problem using groups.
I ended up with this code :
var group = getGroupByName(name);
group.parentNode.removeChild(group);
...
function getGroupByName(name) {
var container = document.getElementById("container");
var groups = container.getElementsByTagName("g");
for(var i=0; i<groups.length; i++) {
if(groups[i].getAttributeNS(null, "name") === name) {
return groups[i];
}
}
return null;
}
Where container is my main SVG element.
This is tried and true. Works properly.
EDIT
As pointed out in the comments. You can find this fiddle that works. Similar to your example. It creates 4 rectangles and removes the 2 first ones.
If you want to remove the first element you have to specify this :
$("rect").first().remove();
Or if you want to do something with ALL of your rectangles you could approach this with something of the sort :
$("rect").each(function() {
... //could remove them here
}
Edit 2
According to last comment, as long as you have the reference to the object, you can use it's variable to remove it.
This updated fiddle will show you that using lastRect you can remove this last rectangle that was added.
I found that doing a .find("*") helped a lot, I'm guessing it flattens the DOM out and thus ignores any nesting complexities that jQuery can't handle (perhaps... this is my theory at least).
So for example this removes anything other than rect, g, svg elements.
$("svg").find("*").not("rect, g").remove();
A jSFiddle showing find() and removing svg elements

jQuery creates incorrect element for image tag

I've been using jQuery to create HTML elements and then adding them to an XML document, like this:
var doc = $($.parseXML('<?xml version="1.0" encoding="UTF-8"?><root/>'));
var docRoot = doc.find("root");
var childEl = $("<child>");
docRoot.append(childEl);
var imageEl = $("<image>");
docRoot.append(imageEl);
var xmlString = doc.context.xml ||
new XMLSerializer().serializeToString(doc.context);
$("#xml").text(xmlString);
This is the output (on Chrome 24):
<?xml version="1.0" encoding="UTF-8"?>
<root>
<child xmlns="http://www.w3.org/1999/xhtml"></child>
<img xmlns="http://www.w3.org/1999/xhtml" />
</root>
Here's the JSFiddle link. Unfortunately, I'm having two problems.
When I try to create an element with the name like child, it correctly creates a element with the tagName child. However, if I use the name image, for some reason jQuery thinks I want to make an img element. How do I stop jQuery from doing this?
All child elements get the attribute xmlns="http://www.w3.org/1999/xhtml" added automatically, even though the document I'm generated is not an XHTML document. How do I stop this from happening?
Update:
The image tagName problem appears to be a problem with the DOM, not jQuery, as this code demonstrates:
var el = document.createElement("image");
$("#output").append(el.tagName); // Outputs "IMG"
image is a synonym for img. document.createElement('image') actually creates an img element, like I explained in this question.
Still, all hope is not lost. When you pass an HTML/XML string to jQuery, the second argument is the owner document of the elements to be parsed.
Since you already created an XML document object in your first step, I believe that
var imageEl = $("<image />", doc[0]);
will use the createElement method of the XML document and create the correct element.
Note: Internally, jQuery uses jQuery.parseHTML when passed such a string, so this method might not always work. It looks like though that jQuery consistently uses the passed in document (context). It should certainly work for single tags.
Safer (and maybe easier?) might be to just use:
var imgeEl = $(doc[0].createElement('image'));

How can I programmatically copy all of the style attributes from one DOM element to another

I have a page with two frames, and I need to (via javascript) copy an element and all of its nested elements (it's a ul/li tree) and most importantly it's style from one frame to the other.
I get all the content via assigning innerhtml, and I am able to position the new element in the second frame with dest.style.left and dest.style.top and it works.
But I'm trying to get all the style information and nothing's happening.
I'm using getComputedStyle to get the final style for each source element as I loop through each node then and assigning them to the same position in the destination nodelist and nothing happens to visually change the style.
What am I missing?
With getComputedStyle, you can get the cssText property which will fetch the entire computed style in a CSS string:
var completeStyle = window.getComputedStyle(element1, null).cssText;
element2.style.cssText = completeStyle;
Unfortunately, getComputedStyle isn't supported by Internet Explorer, which uses currentStyle instead. Doubly unfortunate is the fact that currentStyle returns null for cssText, so the same method cannot be applied to IE. I'll try and figure something out for you, if nobody beats me to it :-)
I thought about it and you could emulate the above in IE using a for...in statement:
var completeStyle = "";
if ("getComputedStyle" in window)
completeStyle = window.getComputedStyle(element1, null).cssText;
else
{
var elStyle = element1.currentStyle;
for (var k in elStyle) { completeStyle += k + ":" + elStyle[k] + ";"; }
}
element2.style.cssText = completeStyle;
Have you tried cloneNode? It can copy the element and all of its children in one fell swoop.
http://www.w3schools.com/dom/met_element_clonenode.asp
Well I gave up the original tack of trying to get the [computed] style information out of the source tag, I just never got it to work.
So instead I tried this, and it did work...
First I grabbed the -style- tag from the source frame, then I appendChilded it to the end of the -head- tag of the second frame.
For which this proved useful...
How do you copy an inline style element in IE?
Then I made a few nested div elements, each having an id or style class I needed to inherit so that the hierarchy matched the first frame.
Then I shoved the innerhtml of the source frame's tag that I wanted to copy and then finally appendChilded it to the body of the second frame, and magically, it all worked.
var topd = doc.createElement('div'); // make a div that we can attach all our styles to so they'll be inherited
topd.id = 'menuanchorelement';
// shove our desired tag in the middle of a few nested elements that have all the classes we need to inherit...
topd.innerHTML = "<div id='multi-level'><ul class='menu'>" + dh.innerHTML + "</ul></div>";
doc.body.appendChild(topd); // voila

Remove all the children DOM elements in div

I have the following dojo codes to create a surface graphics element under a div:
....
<script type=text/javascript>
....
function drawRec(){
var node = dojo.byId("surface");
// remove all the children graphics
var surface = dojox.gfx.createSurface(node, 600, 600);
surface.createLine({
x1 : 0,
y1 : 0,
x2 : 600,
y2 : 600
}).setStroke("black");
}
....
</script>
....
<body>
<div id="surface"></div>
....
drawRec() will draw a rectangle graphics first time. If I call this function again in an anchor href like this:
...
it will draw another graphics again. What I need to clean all the graphics under the div and then create again. How can I add some dojo codes to do that?
while (node.hasChildNodes()) {
node.removeChild(node.lastChild);
}
node.innerHTML = "";
Non-standard, but fast and well supported.
First of all you need to create a surface once and keep it somewhere handy. Example:
var surface = dojox.gfx.createSurface(domNode, widthInPx, heightInPx);
domNode is usually an unadorned <div>, which is used as a placeholder for a surface.
You can clear everything on the surface in one go (all existing shape objects will be invalidated, don't use them after that):
surface.clear();
All surface-related functions and methods can be found in the official documentation on dojox.gfx.Surface. Examples of use can be found in dojox/gfx/tests/.
while(node.firstChild) {
node.removeChild(node.firstChild);
}
In Dojo 1.7 or newer, use domConstruct.empty(String|DomNode):
require(["dojo/dom-construct"], function(domConstruct){
// Empty node's children byId:
domConstruct.empty("someId");
});
In older Dojo, use dojo.empty(String|DomNode) (deprecated at Dojo 1.8):
dojo.empty( id or DOM node );
Each of these empty methods safely removes all children of the node.
From the dojo API documentation:
dojo.html._emptyNode(node);
If you are looking for a modern >1.7 Dojo way of destroying all node's children this is the way:
// Destroys all domNode's children nodes
// domNode can be a node or its id:
domConstruct.empty(domNode);
Safely empty the contents of a DOM element. empty() deletes all children but keeps the node there.
Check "dom-construct" documentation for more details.
// Destroys domNode and all it's children
domConstruct.destroy(domNode);
Destroys a DOM element. destroy() deletes all children and the node itself.
const wipeOut = elm => [...elm.childNodes].forEach(child => child.remove());
wipeOut(elm);

Categories

Resources