D3's use of the 'select' and 'selectAll' - javascript

I cannot wrap my head around D3's select statement.
When creating a graph, you would typically do something like this:
svg.selectAll("rect")
.data(my_graph_data)
.enter()
.append("rect")
..//etc.
What I don't understand is the selectAll.
At this point in time, there is NO element to select!
The elements are only appended later based on the data.
Or should I read these kind of D3 statements from right to left?
In that case, why would I need to select the elements?

You need to select the (non-existant) elements because of the subsequent call to .data(). This call instructs D3 to match the data given in the argument to the elements you've selected. If, as in your case, the selection is empty, D3 will create placeholder elements for all of them -- the enter selection. This selection you can then use, as you do, to append new elements.
This .selectAll(...).data(...) pattern is consistent regardless of what you have in your SVG already. As long as you handle all three (enter, update, exit) selections, it will always do the expected thing. In the initial selection, there may be no elements, elements for all of the data, or somewhere in between.
Technically, you don't have to use this pattern when adding new elements initially, it just makes the code more consistent. In particular, you can use this pattern in a function that will update the SVG with new data, regardless of its state (i.e. works the same way initially and when getting and displaying new and changed data).

Related

AmCharts 4 Force Directed Chart - Select Bubble by Parent -> Child Location

Using the demo chart available from the AmCharts website (here), if I know the parent Phoebe, and the child David, how can I programmatically get the David bubble?
My goal is to alter the fill of the bubble, which I can do in an event handler. I just am uncertain of how to select the element I want programmatically, given what I have to work with.
Edit: I realize that I can just traverse graph.data and modify the color attribute, but I can't call graph.invalidateRawData() as that doesn't pick up the coloring change (makes sense). I really would rather not have to force a complete graph redraw!
Thanks!
The easiest way to get a desired node would be to set id data field (it can be the same as your name field) and use series.getDataItemById method to retrieve the data item.
function changeColor(){
let dataItem = networkSeries.getDataItemById(networkSeries.dataItems, "Fifth");
dataItem.node.circle.fill = am4core.color("#00ff00");
// change color of all children
dataItem.children.each(function(child){
child.node.circle.fill = am4core.color("#00ffff");
})
}
Here is a demo:
https://codepen.io/team/amcharts/pen/mZMYKx
The demo also illustrates how access and change color of all children of the data item.

Chart does not reflect active entries unless I set it to array.slice() in the markup -- this will disable selecting points - Stackblitz inside

On the line chart there is the select event where you can select points & legend items.Line chart has activeElements input property which takes an array of the active elements.
Line chart will not react to changes to the activeElements array unless you enter it in the markup using .slice() either as a local variable OR redux state object:
[activeEntries]="array.slice()"
[activeEntries]="(array | async).slice()"
This works but then the (select) event is not triggered when you click on points anymore.
If you however remove .slice() -- you will be able to select both -- but the chart will no longer react to activeEntries changes no matter what.
This includes having the activeEntries a redux state object where the whole state is a new object on each change -- calling changeDetection everywhere does not affect it at all.
Here's a stackblitz:
https://stackblitz.com/edit/angular-ngx-charts-testing-stuff?file=src/ -- stackblitz.
I have commented the code & markup everywhere so you can understand what I'm trying to do in the code.
I'd like to be able to click on points + legend when using .slice() or have the chart react to changes to activeEntries array.
It actually takes a whole lot more to highlight the active entries and it was purely a coincidence that calling .slice worked at all.
What I needed wasn't actually a feature yet.

Aurelia JS - multi level binding/update?

I'm not sure whether I'm using the right terminology here - but here is an example:
https://gist.run/?id=57ed46429e4583eb4c3fb11814451a55
This is how it looks like in Chromium:
Basically, the entries on top (red outline) are a visualization of an array as the "first-level" data display; here one can toggle each element's selection, and make a multi-element selection (red background). The array that is the source of the "first-level" display is mydata in first-level-items.js.
Those items that are selected in "first-level", are then shown again in "second-level" (green outline); here the same information of name and value is displayed, although a bit differently. Here also one can toggle an elements selection - but only one "second-level" element can be selected. The array that is the source of the "second-level" display is myseldata in second-level-items.js.
The intent here, is that once a "second-level" selection has been made, a slider appears, to change the .value property of the particular object which is the selected array element.
My original question (which is why I started this post/example at all), was:
How do I ensure that whenever the slider is changed, the value is updated in both second-level and first-level display?
... however, for reasons beyond me, this in fact does work somewhat in this gist.run example (but it still doesn't work in my actual project, which forced me to come up with the example to begin with). Also it only works somewhat, in the sense that when loading the example page at first, after making first and second level selections, and then changing the slider, the .value will be updated in both first- and second-level display. But as soon as I try deselecting on second level - or changing the selection on second level - then updating stops. So, I guess this question still stands...
After a second-level selection has been made, deselecting on second level (by clicking to toggle) does NOT remove the slider; how can I have it behave like that?
The update happens only on the slider's onChange - basically, while you drag and slide, this component emits onSlide, but it will generate onChange only at the end when the mouse is released (that is, when the sliding has stopped). How can I update both first- and second- level display while the slider is sliding?
And finally - is this how this kind of a problem is best addressed in Aurelia? That is - I currently have one array in first-level-items.js; first-level-items.js then has a singleton reference to second-level-items.js, so it can call a method within it, to change a filtered copy of the array on the second level, which then serves as a source both for second-level display and the slider... Is there a better way to organise this?
Boy, this was a pain, but here's what I think is the solution:
https://gist.run/?id=c09fea3b82a9ebc41e0a4c90e8665b04
Here are some notes:
Apparently, there is something wrong applying if.bind or show.bind on the input element of the slider - instead, the input element should be put in an enclosing div, and the div should have if/show.bind applied
Furthermore, if.bind should not be used, as it re-instantiates the slider element - use show.bind so we can get a reference to the slider widget at start already, even if it is hidden
Seemingly, using TaskQueue in attached() is the only way to get a reference to the slider at start
Once we have a reference to the widget, re-apply it on each second level element, whenever they change
Do not set this.myselChanging to null to specify no target of the slider (simply count on hiding the slider appropriately)
For a continuous change (onSlide), simply use this.myselChanging.value = e.value; in the handler - both first-level and second-level values will be changed
Beyond this, it seems arrays are copied by reference, so the multi-level update happens without further intervention...
Though, would still love to know what is the proper way to do this...

Paper.js - Get first ''selected item'' from selectedItems

I'm trying my way around building a graphics editor with Paper.js
I would like to select the first(1st) element that was picked using a selection tool(either Shift+Click, or dragging a selection box). Direct-click detection is done via hit-testing and selection box via getIntersection
If it's Shift+Click its the first selected element. Which element was clicked is found with HitResult
If its selectionBox, the first intersection? result of the selection box.
Is it possible to get this?
I do a FOR loop to get all the elements in the Paper.js selectedItems array which returns all the selected items on the canvas.
I've tried this:
var selected = paper.project.selectedItems;
var firstSelected = selected[0];
but this doesn't return what i need.
The above code snippet fetches an array that is agnostic to which element was selected first. It simply orders the selectedItems in the array starting from the ''oldest drawn path''.
What i need is a way to find out which is the 1st element that was selected.
Either the 1st that get's ''touched(intersected)'' with the selection
rectangle
Or the first that was clicked while in Shift+Click
functionality(Select more than one element with click).
The reason for this is that i would like to add to my editor, align-relative-to functionality.
In order to have a correct UX, the user must have the ability to align an element relative to another that he sets as the ''reference item''. Therefore i need to know the 1st element selected relative to others in order to use it as the ''reference item''.
Any ideas ?
Here is a working example of what I understood you want to achieve for the selection (without the align-relative-to functionality).
And here is the code (under SelectPaperJS) https://c9.io/arthursw/oiio/
It should not be too hard to make something similar on Stylii (since you're using it).
You can have an array to keep track of the order of selection of your items. For example in the mousedown function of the direct select tool (from line 789 of editor.js) you can add the newly selected element to this array (line 800). Same thing when you select with the rectangular selection tool.

How can I update a bar chart in d3.js?

I'm trying to update a d3.js bar chart whenever a user clicks a button.
I've been tying to add in a transition somewhere as show here but it doesn't update my data on the fly.
I'm working with this jsfiddle.
I've tried something like the following code but it doesn't update my data:
btn.onclick = function(){
data.push({date: new Date(2013, 3, 10), total: 78});
updatePlot();
}
function updatePlot(){
d3.select('#graphTest').transition();
}
How can I change my updatePlot method so that it updates the data in my bar graph?
It looks like you're skipping a few steps. D3 doesn't actually do a ton of magic for you behind the scenes. You still need to push the data around and update the DOM yourself. D3 just provides a convenient programming model for managing this process.
So, with any visualization you've got the following interesting pieces:
Data array
DOM nodes
Data array elements bound to DOM nodes.
Attributes and styles on DOM nodes.
Your code is updating the data array but it's not managing any of the other parts. You need to drive your updated data back in to the DOM (and specifically the attributes/styles that make the nodes visible).
So update plot will need to do the following:
Call selection.data to bind the new data to the existing DOM. You can defined a key function to control how data maps to DOM nodes.
Handle the enter, exit and update selections however you like.
Transition the attributes and styles based on the new data.
For #3, this isn't just a matter of calling selection.transition, but instead you need to think about which attributes and styles you wish to transition. Calling attr and style on a transition is exactly like calling them on a selection, except the DOM gets updated many times with interpolated values instead of just once with the final value.
To understand this process better you should read Thinking with Joins and Working with Transitions.

Categories

Resources