I have nodes variable that is an array with all nodes from graph. Each node has a SVG g (group) SVG element in DOM.
To select the data having the DOM element I do d3.select(myElement).data()[0], but how can I get the DOM element having the data?
You can do this using D3's data matching. The idea is to have the data "identify" the correct element for you.
d3.selectAll("allMyElements").data([datumIwantElementFor], suitableKeyFunction);
Complete demo here.
To be clear, I do not recommend taking this approach. This is prone to breaking when you add more elements to the document and accidentally select some of those, use the same data in more than one place, or change the data format. It will lead to bugs that are really hard to spot unless you know what you're looking for.
A better approach is to assign an ID or class from that data to each element that you bind the data to so that you can select it directly when you have the data. This will also be more efficient.
Related
I have some hierarchical data which is being packed to form a bubble chart, this works fine however when I update the data and apply a new pack the wrong elements are updated so they transition to the wrong location within the circle. It seems to be updating the data based on its index but I can’t find any way to tell it which data applies to which existing element. Is there a mechanism to link new data with existing elements, for example an ID or anything?
This is noticeable if my hierarchy is one level and then later split into groups. Some of the existing circles become the parent group circles rather than creating new ones for it. I do have enter/exit functions but it just does a dumb update as there seems to be no way of linking the existing elements to the new data.
I’m sure there must be a way to bind the two together but for the life of me I can’t find it. Thanks!
Mehdi answered this in the comments but adding here for anybody else with the same issue.
I was missing a key in the data binding reference.
Instead of applying the data using .data(data) I needed to add an additional key so that it could be bound properly like .data(data, function(d) { return d.data.id; })
I'm getting used to d3, but seem to be having trouble with more advanced structures. I'm fairly sure there's some subtlety or concept I am not fully appreciating. I want to be able to mirror a changing hierarchical data structure with a changing hierarchical element structure.
My data structure is 3 groups, each with 3 items. Each group and item has a unique key, extracted using a key function in the data() call.
I build the structure, and I can remove a top-level item; .exit().remove() works just fine on that selection. BUT, modifying or removing any sub-item is simply not reflected in the generated element structure.
Full (non-)working jsFiddle here!: http://jsfiddle.net/eu95R/2/, and the all-important enticingly beautiful screenshot:
The problem is that your definition of groups is using svg.enter() and the subselection is made on groups. That is, you're not seeing a change because groups in this case is empty (no enter selection for the SVGs) and therefore there's no subselection.
To fix, simply do the subselection based on e.g. svg (there are a number of ways to fix this -- not saying that this is necessarily the best one). As you are appending the elements to a g within the SVG, the selector would be svg.selectAll("g").selectAll("text.item")....
Complete demo here.
I am appending large amounts of table row elements at a time and experiencing some major bottlenecks. At the moment I am using jQuery, but i'm open to a javascript based solution if it gets the job done.
I have the need to append anywhere from 0-100 table rows at a given time (it's actually potentially more, but I'll be paginating anything over 100).
Right now I am appending each table row individually to the dom...
loop {
..build html str...
$("#myTable").append(row);
}
Then I fade them all in at once
$("#myTable tr").fadeIn();
There are a couple things to consider here...
1) I am binding data to each individual table row, which is why i switched from a mass append to appending individual rows in the first place.
2) I really like the fade effect. Although not essential to the application I am very big on aesthetics and animations (that of course don't distract from the use of the application). There simply has to be a good way to apply a modest fade effect to larger amounts of data.
(edit)
3) A major reason for me approaching this in the smaller chunk/recursive way is I need to bind specific data to each row. Am I binding my data wrong? Is there a better way to keep track of this data than binding it to their respective tr?
Is it better to apply affects/dom manipulations in large chunks or smaller chunks in recursive functions?
Are there situations where the it's better to do one or the other? If so, what are the indicators for choosing the appropriate method?
Take a look at this post by John Resig, it explains the benefit of using DocumentFragments when doing large additions to the DOM.
A DocumentFragment is a container that you can add nodes to without actually altering the DOM in any way. When you are ready you add the entire fragment to the DOM and this places it's content into the DOM in a single operation.
Also, doing $("#myTable") on each iteration is really not recommended - do it once before the loop.
i suspect your performance problems are because you are modifying the DOM multiple times, in your loop.
Instead, try modifying it once after you get all your rows. Browsers are really good at innerHTML replaces. Try something like
$("#myTable").html("all the rows dom here");
note you might have to play with the exact selector to use, to get the dom in the correct place. But the main idea is use innerHTML, and use it as few times as possible.
This is a bit of a vague notion which I have been running over in my head, and which I am very curious if there is an elegant method of solving. Perhaps it should be taken as a thought experiment.
Imagine you have an XML schema with a corresponding XSL transform, which renders the XML as SVG in the browser. The XSL generates SVG with appropriate Javascript handlers that, ultimately, implement editing-like functionality such that properties of the objects or their locations on the SVG canvas can be edited by the user. For instance, an element can be dragged from one location to another.
Now, this isn't particularly difficult - the drag/drop example is simply a matter of changing the (x,y) coordinates of the SVG object, or a resize operation would be a simple matter of changing its width or height.
But is there an elegant way to have Javascript work on the DOM of the source XML document instead of the rendered SVG? Why, you ask? Well, imagine you have very complex XSL transforms, where the modification of one property results in complex changes to the SVG. You want to maintain simplicity in your Javascript code, but also a simple way to persist the modified XML back to the server.
Some possibilities of how this may function:
After modification of the source DOM, simply re-run the XSL transform and replace the original. Downside: brute force, potentially expensive operation.
Create id/class naming conventions in the source and target XML/SVG so elements can be related back to each other, and do an XSL transform on only a subset of the new DOM. In other words, modify temporary DOM, apply XSL to it, remove changed elements from SVG, and insert the new one. Downside: May not be possible to apply XSL to temporary in-browser DOMs(?). Also, perhaps a bit convoluted or ugly to maintain.
I think that it may be possible to come up with a framework that handles the second scenario, but the challenge would be making it lightweight and not heavily tied to the actual XML schema. Any ideas or other possibilities? Or is there maybe an existing method of doing this which I'm not aware of?
UPDATE: To clarify, as I mentioned in a comment below, this aids in separating the draw code from the edit code. For a more concrete example of how this is useful, imagine an element which determines how it is drawn dependent on the value of a property of an adjacent element. It's better to condense that logic directly in the draw code instead of also duplicating it in the edit code.
We have done this in our in-browser XML editor Xopus. You can see an example here.
We load the XML source document, an XML Schema and an XSLT that outputs HTML+SVG. Internally we rewrite your XSLT into XSLT' which will allow us to track the output HTML+SVG back to the original XML that was the context in the XSL to output that HTML or SVG node. This is the framework you're referring to.
To facilitate editing we position a cursor over your XSLT output and update the XML DOM when you type or insert elements. After each change we will rerun the XSLT. For performance reasons we will compare the XSLT output with the previous output and apply the changes using HTML DOM operations to the WYSIWYG view.
We diff the two XSLT outputs using another XSLT. We can do so because we have modified the original XSLT (resulting in XSLT') so that is will output unique and persistant IDs for each output node. So all new nodes will have new IDs and missing IDs are removed nodes.
XSLT' is the rewritten version of the original XSLT you provide. It is still XSLT, but we've added a few things to it so it outputs IDs for all output nodes (we do so with yet another XSLT). Other than that it is functionally equivalent to your original XSLT.
Maybe use can AJAX: Instead of editing the document locally, send editing commands for the original XML to the server which transforms again and then sends the new SVG back.
The main problem here will be what happens when you update the SVG element on the current page. Will drag'n'drop still feel smooth? If not, then you might have to resort to some mix of the two methods: Drag the SVG node using JavaScript and when the user drops the node, send an update to the server for a new SVG.
You will want to avoid trying to synchronize updates in XML and the local SVG (which would mean to replicate part of the XSLT in JavaScript -> stay on one world, don't mix).
I'm trying to optimize a sortable table I've written. The bottleneck is in the dom manipulation. I'm currently creating new table rows and inserting them every time I sort the table. I'm wondering if I might be able to speed things up by simple rearranging the rows, not recreating the nodes. For this to make a significant difference, dom node rearranging would have to be a lot snappier than node creating. Is this the case?
thanks,
-Morgan
I don't know whether creating or manipulating is faster, but I do know that it'll be faster if you manipulate the entire table when it's not on the page and then place it on all at once. Along those lines, it'll probably be slower to re-arrange the existing rows in place unless the whole table is removed from the DOM first.
This page suggests that it'd be fastest to clone the current table, manipulate it as you wish, then replace the table on the DOM.
I'm drawing this table about twice as quickly now, using innerHTML, building the entire contents as a string, rather than inserting nodes one-by-by.
You may find this page handy for some benchmarks:
http://www.quirksmode.org/dom/innerhtml.html
I was looking for an answer to this and decided to set up a quick benchmark http://jsfiddle.net/wheresrhys/2g6Dn/6/
It uses jQuery, so is not a pure benchmark, and it's probably skewed in other ways too. But the result it gives is that moving DOM nodes is about twice as fast as creating and detroying dom nodes every time
if you can, it is better to do the dom manipulation not as actual dom manipulation, but as some sort of method within your script and then manipulating the dom. So rather than doing what is called a repaint on every single node, you clump what would have been your repaint on every single node into its own method, and then attach those nodes into a parent that would then be attached to the actual dom, resulting in just two repaints instead of hundreds. I say two b/c you need to cleanup what is in the dom already before updating with your new data.