How to add back to DOM an existing D3 SVG chart? - javascript

Hi have a D3 SVG object:
d3.select("#chart").append("svg")
.attr('id', 'chartId' + idx)
To which I append rects to create a bar chart.
I would like to be able to add and remove it from the DOM.
I am able to remove it easily with:
d3.select('svg#chartId' + j).remove();
How can I re-append the entire SVG chart I just removed?

D3 doesn't support reattaching nodes that have been deleted.
selection.remove() doesn't completely destroy the node though, it acts quite similarly to jQuery's detach() method. (Using jQuery with SVG elements never ends well)
The good news is that this is pretty easy with the built-in SVG DOM.
// .remove() returns the detached node.
var el = d3.select('.selection').remove();
var nativeEl = el[0][0];
nativeEl.parentNode.appendChild(nativeEl);
Usually it would probably just be easier to show and hide these elements, but in cases like re-arranging the order of elements post-render, this can be pretty useful.

Use detach() instead of remove()
var refToElelment = $(d3.select('svg#chartId' + j)).detach();
OR
$('#chartId' + j).detach()
Later..
$('#target').append(refToElelment);

Related

Using svg.js, how can I access the child nodes of an element?

I am accessing an existing DOM SVG element in my HTML using
svgEle = SVG.adopt(svgDocument.getElementById('svg'));
How can I access child elements of svgEle without adopting them individually?
I have tried
elementById = svgEle.select("#" + id);
and
elementById = svgEle.get(id);
In SVG.js 3.0+ the API changed and you should use .find() or .findOne() methods instead of .select().
See reference here: https://svgjs.dev/docs/3.0/referencing-creating-elements/#using-css-selectors
You use select(<CSS Selector>).
svgEle = SVG.adopt(document.getElementById('svg'))
svgEle.select("#keys")
.stroke('yellow')
SVG.get(<ID>) only works for SVG elements created by SVG.js.
Here is jsfiddle to illustrate: https://jsfiddle.net/dotnetCarpenter/yy2opz8s/2/

Select cloned element by data attribute using jQuery

So I have cloned an element and added some data to it. Now I need to select that cloned element by its data attribute. The problem is that I can't find, select that cloned element based on data attribute. Any ideas?
Here is the fiddle http://jsfiddle.net/fjckls/hho6k86a/
This doesn't work:
var clone = $(".clone").clone(true);
clone.html("this is clone")
clone.data("info", "this-is-clone");
$(".clone-holder").append(clone);
// nothing is selected
var cloned = clone.find("[data-info='this-is-clone']");
cloned.css("color", "red")
#fjckls it's just a work-around by using attribute instead of data, it
does not explain why your code does not work. This kind of answer of
course won't help community much. – King King
I have accepted #Hendra Lim answer for the moment - and I understand its kind of a workaround - not using data attribute but instead creating new custom attribute. If someone has a "proper" solution of using data in my scenario then I would reconsider the accepted answer. For the time being it works so I'll leave where it is.
try this :
var clone = $(".clone").clone(true);
clone.html("this is clone")
clone.attr("info", "this-is-clone");
$(".clone-holder").append(clone);
var cloned = $(".clone-holder").find("[info='this-is-clone']");
cloned.css("color", "red")
here is the working example here
what u do wrong, is u try to find cloned element inside ".clone" element, instead u have to find cloned element in ".clone-holder" element coz u append ur clone element there. and replace clone.data to clone.attr.

How to pick elements in d3 with a specific property value

I am having items painted using d3 on browser. I want to highlight some of them depending on their property. For example, I have groceries, soaps in which soap elements will have type as [for_bath (OR) for_cloth_wash]. I want to select those elements specific to for_bath in all soaps and groceries combined and painted together on same screen.
How ?
Also, my another doubt is document.getElementById() is not working inside d3's selections code. Am I true or an oversight ?
EDIT
var data = {"soaps":{"lux":"bath", "cinthol":"bath", "rin","cloth_washing"},
"shoes":{"valentine":"teens", "bootie":"kids", "kuuch":"kids"}};
// Now I want to show all bath soaps highlighted, may be with a circle around them.
var svg = d3.select("svg")
.selectAll(".items")
.data(data).enter()
.append("circle")
// highlighting styles
;
Here I want to select bath soaps and round them up.
You haven't given us any code, so I'm going to guess here. What you're probably looking for are CSS attribute selectors. So if you want to select elements which have the attribute soap set to for_bath, you would do
d3.selectAll("[soap=for_bath]");
This is for DOM elements only. If you're talking about data elements, then you can use the .filter() method:
data.filter(function(d) { return d.soap == "for_bath"; });
Regarding your second question, I'm not sure what you mean. The arguments to d3.select() or d3.selectAll() are DOM selectors, so document.getElementById() doesn't make sense here. You can however certainly use it other functions:
d3.selectAll("something").each(function() {
d3.select(this); // the current element
document.getElementById("something"); // another element
});

jquery - mix variable referencing a dom element with selector

I usually create variables for frequently reused DOM elements like this:
var $dom_element = $('#dom_element);
where this is my setup:
<div id="dom_element">
<div class="child_element">
<div class="child_element">
</div>
what I'm wondering is if I can mix this variable with a subselector to get child elements. I guess it would be something like this:
var $child_element = $($dom_element + ' .child_element);
And if so, is there any speed benefit to doing this versus just saying:
$('.child_element);
considering the fact that both of these elements might be deeply nested in a large site?
With
var $dom_element = $('#dom_element);
I would use the following to get the child elements
var $child_element = $dom_element.find(".child_element");//I prefer this one, it is easier to read.
or
var $child_element = $(".child_element", $dom_element);
From my research/reading, it appears that setting an element to a variable is best if you are going to reference it many times. That way jQuery does not have to search the DOM many times.
Regarding your selectors, you can get yourself into trouble when using a bare class as a child selector. What if you have multiple child nodes using that same class?
.find() works, as others have suggested. You could alternatively use .children():
var $kids = $dom_element.children('.child_element');
http://api.jquery.com/children/
The difference between .find() and .children() is that .children() will only look one level down the DOM tree. .find() will recursively run through all possible child nodes to match your selector.

Is there an easy way to clear an SVG element's contents?

In HTML, I can clear a <div> element with this command:
div.innerHTML = "";
Is there an equivalent if I have an <svg> element? I can't find an innerHTML nor innerXML or even innerSVG method.
I know the SVG DOM is a superset of the XML DOM, so I know I can do something like this:
while (svg.lastChild) {
svg.removeChild(svg.lastChild);
}
But this is both tedious and slow. Is there a faster or easier way to clear an SVG element?
If you're using jQuery, you can just do
$("#svgid").empty();
This deletes all child elements of the svg while leaving its other attributes like width and height intact.
You already gave one answer: you can always just loop over all children and remove them. If you think that you have too many child nodes then maybe you want to replace the svg node by an empty one. If your svg node has some attributes you may use a tag where you place all the child nodes and then just replace the node with an empty one.
Use d3.js. This will remove all contents nodes from svg.
svg.selectAll("*").remove();
I agree to use the clone and replace the element with the cloned one.
Only one line code:
svg.parentNode.replaceChild(svg.cloneNode(false), svg);
I've tried svg.text("") and it seems to work. Clears out all the inner text, keeps the attributes.
No need to loop, just assign an empty string
svg.innerHTML = "";
If you want to keep defs of your svg as me use this
function clear(prnt){
let children = prnt.children;
for (let i=0;i<children.length;){
let el = children[i];
if (el.tagName!=='defs'){
el.remove();
}else(i++);
}
}
You can use the clone and replace the element with the cloned one.
var parentElement = svg.parentElement
var emptySvg = svg.cloneNode(false);
parentElement.removeChild(svg);
parentElement.appendChild(emptySvg);
This will append the svg at the end,
you might want to get the element before and append accordinaly
use this? http://keith-wood.name/svg.html
there's also raphael: jQuery SVG vs. Raphael
I'd be tempted to trawl through and see who they're doing their .destroy() methods.
element = document.getElementById("elementID");
element.parentNode.removeChild(element);
I got the idea from http://www.carto.net/svg/manipulating_svg_with_dom_ecmascript/

Categories

Resources