d3 get rect attribute by id - javascript

I just stuck on a problem when trying to select an attribute of a rect object within a svg.
This is the rectangle I want to get a value from:
<rect id="2" x="13.761467889908257" y="50" width="49.31192660550459" height="50" fill="rgb(43,0,0)"></rect>
in order to calculate the x-position of another rectangle I need the x-value.
I tried some different ideas like:
svg.selectAll("rect")
.select("id",2); // 1st version
.select("id","2"); // 2nd version
.select("#2"); // found in another d3 tutorial -> not working for me
and a lot more. Is there any way to do this?
// If you need more of my code just write it in a comment I wasnt sure how much to write in order to NOT write too much

According to HTML 4, id's can't start with a number. HTML 5 did change that, but CSS and d3 still don't really support that. So if it's not necessary, it may be easier to just change the id value to not start with a number. This problem is why the #2 selector isn't working, I think.
If you did want to keep id = 2, you can probably use the attribute selector to do it in one line, like this:
svg.select("rect[id='2']")
You can read what Mike Bostock has to say about id's starting with numbers here.
Then, once you have your selection, just do .attr("x").

Try calling directly d3.select('#2');

Related

Adding Data Label and Marker to the Line chart using d3

I have tried various approaches adding the marker and label to the line chart.
I tried with enter() method to add circles to the linechart. what I am able to get is the beginning or first point at the group level as mentioned in the snapshot.
Here, is the jsfiddle which I have tried till now.
Please advice, If there is any further information required.
Following is the snippet which I have tried to create marker
g.append('circle')
.attr("class" ,"circle")
.attr('cx',function(d) { return xScale(d[0]) ; })
.attr('cy',function(d) { return xScale(d[1]) ; })
.attr('r',22);
I second #TomShanley answer but you should not be using the enter .data[[data]] as you are not using the data itself. You should instead just add svg elements using d3. Later on you can add the data as needed to create the circles.
You should also not have <svg> elements inside <g> or others <svg>
Here is the updated fiddle:https://jsfiddle.net/mghays6a/1/
The enter selection that you are using for the circles, is based off the append for the line that uses .data([data]). While this is OK for lines/paths (as you want one element), this won't work when you want separate elements per datum.
You should do a separate selectAll("circle").data(data).enter()...etc, where the data is not in the square brackets. This will give you separate elements for each circle.

"mouse:over" not firing and "mouse:down" returning undefined target using fabricJS

Here is my issue; I am trying to display "markers" and on mouse over/out/click give some actions.
The problem is that no event gets fired on over at all and when on click (down) in the console I get some feedback, but not of the element per say (target == undefined).
My different shapes are groups in a group called "marker".
And I group everything with the following:
marker.add(plateShape, plateLabel, line, indicator);
When using
marker.addWithUpdate(plateShape, plateLabel, line, indicator);
I get some feedback (over being finicky at best) but the layout get completely messed up.
You can comment/uncomment Line 86 to check the behaviour in the following fiddle.
https://jsfiddle.net/u7x7az1j/5/
Thank you for your help! :)
The problem comes from your use of add.
Replace add with addWithUpdate and be aware that addWithUpdate takes one parameter at time.
marker.addWithUpdate(plateShape);
marker.addWithUpdate(plateLabel);
marker.addWithUpdate(line);
marker.addWithUpdate(indicator);
i tried on your fiddle and it works.
Consider replacing rect + triangle + line with a simple parametric fabric.Path you can easily build.

hide one of the axes of a parallel-coordinates plot

i'm representing some data with the parallel coordinates library (based on d3.js) ( https://github.com/syntagmatic/parallel-coordinates#parallel-coordinates )
main functional part of the code is the following:
var parcoords = d3.parcoords()("#example") //#example is the div for the drowing
.data(eingabe) // eingabe is the var, which contains the data
.render()
.reorderable()
.shadows()
.brushMode("1D-axes")
so far it works fine :)
but now i want to hide one specific axis of the parallel coordinates; i only don't want to show it.
I want that the axis is completeley removed, so that it takes no place and the lines don't be affected of the values of this dimension, but i don´t want to do that by manipulating the data. i only want to manipulate the plot (thats the reason i called it hide).
i've searched on the api description but didn't find something. I searched in the internet but didn't find anything, too. But i think i remember that i've seen a peace of code, where the first axis was hidden, but i can't find it again.
Can anybody tell me, how i can hide an axis or where i can find the solution?
thank you, greetings
Jones
You're missing a .hideAxis(array) method call, where the array is a list of keys of the exact column names in your dataset that you want to hide. This method does exactly what you said you wanted:
"...hide one specific axis of the parallel coordinates; i only don't want to show it."
"I want that the axis is completley [sic] removed, so that it takes no place"
"the lines dont [sic] be affected of the values of this dimension, but i don´t want to do that by manupulating [sic] the data. i only want to manipulate the plot."
To implement this method, your code would need to be
var parcoords = d3.parcoords()("#example") //#example is the div for the drowing
.data(eingabe) // eingabe is the var, which contains the data
.hideAxis(["col1", "col2"])
.render()
.reorderable()
.shadows()
.brushMode("1D-axes")
Note: .hideAxis can take in a blank array if you don't want to hide any axis, i.e. .hideAxis([])
You can implement this method in some sort of update/redraw function if you want to allow the user to specify which axis(es) to remove; else, removing it completely via the DOM is impossible. This, performing the axis(es) hide via code, is your only option to implement what you want.
To see this method in action, take a look at the API documentation, specifically, their "example1" graph and code at http://syntagmatic.github.io/parallel-coordinates/index.html#example1
Assuming you just want to hide the axes (i.e. make it invisible) and not remove it completely
d3.selectAll("#example0 > svg > g > g.dimension:nth-child(3)").attr("opacity", "0");
Assuming example0 is the id of the wrapper around your svg element. You could also do the same thing directly on your svg element if you have it.
The above would still leave you able to interact with the filter of the hidden axis. To make it non-interactable, use .attr("display", "none");
Don't have a fiddle, but you should be able to copy paste the above line and run it from the console at http://syntagmatic.github.io/parallel-coordinates/ and see the effect on the chart in the Bundling section.
Here is the effect - before and after respectively

How do I test a click on SVG objects using Selenium webdriver?

I am trying to write a code to check the functionality of a click on an SVG object - for example a US state on this URL
http://www.amcharts.com/svg-maps/?map=usa
This works, but is there a better way to do this? Something without physically moving the mouse?
robert = new Robot();
robert.mouseMove(x, y);
// full click once to get focus on the window
robert.mousePress(MouseEvent.BUTTON1_MASK);
robert.mouseRelease(MouseEvent.BUTTON1_MASK);
// then set the filter
robert.mousePress(MouseEvent.BUTTON1_MASK);
robert.mouseMove(x, y);
robert.mouseRelease(MouseEvent.BUTTON1_MASK);
In SVG, everything displayed is a block defined by coordinates. In your case, every state is a block as well and therefore has an XPath which you will be able to use in your Selenium code.
Use your browser developement tool to analyse the code of the page and find which block corresponds to the state you want to click on.
Here is the element corresponding to California.
<path cs="100,100" d="M371.75,174.28l-1.09,4.03l-1.09,4.03l-1.09,4.03l-1.09,4.03l-1.09,4.03l-1.09,4.03l-1.09,4.03l-1.09,4.02l-1.09,4.03l-1.09,4.02l-1.09,4.02l-1.09,4.02l-1.09,4.02l-1.09,4.02l-1.09,4.02l-1.09,4.02l2.98,4.56l3,4.56l3.02,4.56l3.05,4.55l3.07,4.54l3.09,4.54l3.11,4.53l3.13,4.53l2.41,3.75l2.42,3.75l2.44,3.75l2.45,3.74l2.46,3.74l2.48,3.74l2.49,3.73l2.5,3.73l3.46,5.32l3.48,5.31l3.51,5.3l3.53,5.3l3.56,5.29l3.59,5.28l3.62,5.28l3.9,5.63l-0.39,1.82l0.45,2.66l1.95,5.06l0.13,1.14l0.03,0.03l-0.12,1.14l0.89,1.45l2.36,2.25l0.44,0.95l-0.01,0.07l-0.13,0.6l-0.83,0.59l-3.5,1.82l-2.02,1.65l-2.31,2.87l-0.28,2.66l-0.52,2.14l-0.76,1.62l-0.95,1.44l-1.15,1.26l-1.07,0.67l-0.98,0.09l-0.72,1.83l-0.46,3.58l0.59,2.36l1.64,1.14l0.86,1.39l0.08,1.64l-0.47,1.5l-1.03,1.37l-1.39,0.58l-2.61,-0.31l0,0l-0.11,0.19l-2.16,-0.21l-5.38,-0.65l-5.39,-0.67l-5.38,-0.69l-5.38,-0.7l-5.38,-0.72l-5.38,-0.74l-5.38,-0.76l-5.38,-0.77l-0.01,-0.15l0.44,-2.41l-0.65,-1.04l-1.22,0.26l0.24,-3.21l0.62,-1.39l0.21,-1.45l-0.19,-3.74l-1.69,-4.89l-4.56,-6.68l-2.53,-2.49l-1.78,-2.8l-1.32,-0.98l-1.81,-0.63l-0.79,0.87l-1.93,-1.21l0.94,-2.39l-1.17,-3.95l-1.57,-0.8l-4.25,-0.84l-5.11,-3.34l-1.36,-1.55l-0.04,-2.16l-2.15,-2.43l-2.98,-2.62l-2.02,-0.11l-2.43,-0.93l-3.22,-2.19l-2.03,-0.72l-4.14,-0.74l-1.44,-0.67l-0.97,-1.94l-1.29,-1.19l0.85,-1.82l0.29,-1.78l0.6,-1.28l0.15,-3.13l1.28,-2.58l-0.18,-1.11l-0.63,-0.99l-2.33,-1.86l-0.09,-1.53l0.98,-1.82l-0.33,-1.47l-1.82,-1.8l-1.25,-3.28l-2.13,-2.21l-0.34,-2.78l-1.13,-1.98l-0.15,-1.51l-2.06,-5.84l-2.58,-4.85l0.07,-2.34l0.73,-3.02l1.97,-1.4l1.24,-1.37l0.35,-1.49l0.09,-1.14l-0.71,-2.24l-4.53,-2.54L303,265.5l0.82,-3.6l-0.46,-4.07l0.68,-2.35l0.53,-2.61l1.33,-0.21l0.98,0.51l-0.41,0.98l-0.19,1.92l0.81,1.73l0.99,0.94l0.67,1.64l0.68,0.64l0.8,0.34l-0.19,-0.98l-0.31,-0.68l-0.05,-1.93l-0.42,-2.57l-0.88,-1.61l0.04,-2.45l-0.38,-0.69l-0.09,-0.94l1.5,-0.64l1.85,-0.22l2.25,0.46l6.14,2.16l1.5,-0.19l1.04,0.51l0.83,0.16l-1.52,-1.09l-1.01,-0.08l-1.08,-0.45l-2.26,-0.53l-0.83,-0.52l-0.78,-1l-0.63,-0.26l-2.42,0.63l-0.87,-0.42l-1.77,-1.99l-0.89,-0.47l-1.75,0.31l-1.18,3.25l-0.27,2.6l-0.99,-0.02l-0.76,-1.33l-1.45,-1.09l-1.06,-1.33l-1.37,-2.29l-0.8,-0.93l-1.56,1.08l0.16,-0.66l1.06,-1.48l0.69,-2.82l1.02,2.73l-0.05,-1.72l-0.79,-2.11l-0.82,-0.9l-0.31,-3.45l-2.24,-2.71l-1.33,-3.67l-3.05,-6.35l1.05,-4.41l0.06,-5.98l1.68,-2.88l0.6,-2.23l0.24,-3.58l-0.27,-2.07l-2.08,-6.11l-2.43,-4.46l0.29,-2.69l0.58,-2.62l1.49,-2.02l1.42,-2.17l0.68,-0.48l0.1,0.32l-0.3,0.47l0.44,0.31l0.52,-0.99l0.47,-0.45l-0.5,-0.24l0.16,-0.32l0.52,-0.56l2.08,-2.78l1.15,-3.98l2.69,-4.47l0.46,-1.61l0.37,-3.67l-0.06,-2.29l-0.82,-1.88l1.25,-1.95l0.61,-2.05l-0.15,-0.43l4.31,1.38l4.17,1.32l4.18,1.31l4.18,1.3l4.19,1.28l4.19,1.27l4.19,1.26l4.2,1.25l4.2,1.23l4.2,1.22l4.21,1.21l4.21,1.2l4.21,1.19l4.22,1.17l4.22,1.16L371.75,174.28zM327.88,344.91l3.35,2.08l2.11,0l0.21,0.63l-0.36,0.4l-4.66,-0.35l-1.21,-0.95l0.09,-0.83l-0.25,-0.89L327.88,344.91zM319.97,344.03l-0.97,-0.2l-1.4,-0.63l0.65,-0.36l0.91,-0.14l0.18,0.34L319.97,344.03zM324.06,347.59l-1.34,-0.04l-0.88,-0.55l-0.96,-2.47l3.3,0.61l1.15,1.27l0.12,0.3L324.06,347.59zM351.66,367.08l0.52,1.82l-1.27,-0.53l-1.4,-0.26l-0.2,-0.97l-0.11,-1.31l-0.2,-0.38l-0.92,-0.35l-0.04,-0.13l0.04,-0.61l0.34,-0.21l2.62,2.09L351.66,367.08zM330.94,365.63l-0.82,-0.17l-1.06,-0.49l-0.26,-1.31l0.93,0.16l0.8,0.38l0.43,1.09L330.94,365.63zM348.58,379.15l-1.11,-0.07l-1.07,-0.74l-0.49,-2.35l-0.7,-1.92l0.72,-0.31l0.51,1.8l1.67,2.96L348.58,379.15z" fill="#FFFFFF" fill-opacity="0.8" stroke="#000000" stroke-opacity="0.5" stroke-width="0.5"></path>
When you have found it, right click on the element and "Copy XPath".
//*[#id="chartdiv"]/div/div[1]/svg/g[7]/g/g[1]/path[47]
However, do it two more times and you will see that the last node is never the same.
//*[#id="chartdiv"]/div/div[1]/svg/g[7]/g/g[1]/path[5]
//*[#id="chartdiv"]/div/div[1]/svg/g[7]/g/g[1]/path[34]
So we would like to find the <path> in //*[#id="chartdiv"]/div/div[1]/svg/g[7]/g/g[1]/ which the d attribute value starts with M371.75,174.28l. It should be something like:
//*[#id="chartdiv"]/div/div[1]/svg/g[7]/g/g[1]/path[starts-with(#d, "M371.75,174.28l")]
You shall have to repeat all these steps by yourself, because we might not have the same generated SVG.
But because the SVG is using another namespace:
/g becomes /*[name()="g"]
/svg becomes /*[name()="svg"]
/path becomes /*[name()="path"]
Which gives the following XPath:
//*[#id="chartdiv"]/div/div[1]/*[name()="svg"]/*[name()="g"][7]/*[name()="g"]/*[name()="g"][1]/*[name()="path"][starts-with(#d, "M371.75,174.28l")]
In Selenium:
webDriver.findElement(
By.xpath(
'//*[#id="chartdiv"]/div/div[1]/*[name()="svg"]/*[name()="g"][7]/*[name()="g"]/*[name()="g"][1]/*[name()="path"][starts-with(#d, "M371.75,174.28l")]'
)
).click();

Trying to fix table header, getting css issue

Initially I asked this Question and written my own plugin to achieve the same,But i am facing very strange issue regarding to css of table.
After applying the plugin table cells borders are getting dis-sorted.
jsFiddle of the problem: Problem demo
In fiddle you can see that after first cell of the first tr, the header border line and table border line don't line up. I want the border line of thead cells and td cells to line up.
Can anyone tell me how to achieve that?
Lets start by doing a bit of a clean up of the code you posted so I can actually read your code and maintain a firm grasp on reality while we go down this rabbit hole.
If you write clean code, your problems will be exceptionally easier to see.
So lets clean it up and watch as all the problems here reveal themselves.
Step one: Your jsFiddle sets it to run "onDomReady," which basically means you've got $(document).ready(...) calling all the code in the box, which is fine, except you're got another got $(document).ready(...) inside there. Lets change that.
Step two: Lets add some white space and proper indentation in there, and stop using these one letter variable names.
{} are scope brackets, they should indent, not cover everything, they let us know what part of scope something is in.
Don't write .each() loops on one line, this adds no value and makes your code confusing to read.
$t should be called something meaningful, lets try element, because it holds the $(this) element, which is the active element you're working with.
w should be called something meaningful, but since you only use it twice I'm just going with element.width().
o needs to be less ambiguious, lets go with obj.
Step three: Selection structures
if(typeof(i)=='number')o.height=i;
else if(typeof(i)=='object')o=i;
else if(typeof(i)=='undefined')o={height:300}
Break that up, make it readable. Saving lines doesn't make you a better program, writing clean and easily understandable code will.
Why not use the switch-case statement?
switch (typeof(i)){
case "number":
o.height=i;
break;
case "object":
o=i;
break;
case "undefined":
o={height:300};
break;
}
Step four: Don't in-line styles. Just don't. There's no reason to do it, and it makes everyone's life harder.
Instead, lets just place the styles gently into the style sheet where it belongs, and make the parent=$('...') line look like parent=$('<div><div></div></div>').appendTo('body');.
Step five: Closure doesn't pass any value to arguments
After a bit of clean up, we see this block of code:
self.width(self.width() -
function(width){
var parent,child;
if(width===undefined){
parent=$('<div><div></div></div>').appendTo('body');
child=parent.children();
width= (child.innerWidth()) - (child.height(99).innerWidth());
parent.remove();
}
return width;
}()
);
Okay, that's a problem. Lets cut out a few lines to point out the problem here:
self.width(
self.width() -
function(width){
/*...*/
if(width===undefined){
/*...*/
}
return width;
}()
);
So, a quick refresher on this pattern you have here:
(function(arg1){
/*code*/
})(data);
Data gets passed to arg1. Arg1 declaires a variable in the scope local to that function, it doesn't get anything from outside. Outside data is passed in through the set of () that call the function, which your code had left abandoned. Think of it this way:
var msg = function(text) {
alert(text);
};
Then you call it as...
msg("hello world");
What your closure is doing is almost the same thing, except where you define your function, you also call it. Thus...
(function(text) {
alert(text);
})("hello world");
So, you need to pass a value of some sort into there, other wise this whole thing is always undefined. Lets do that. What are we passing? I have no way to be sure. This is why programmers need to add comments to their code.
Step six: Comment your code so people other than yourself will look at this code and have not a damn clue what you truthfuly wanted to do, and can only guess. It's like you posted a 200 point bounty and didn't bother helping people who want to help you. Why are you doing this to yourself, dude? Why couldn't you just go //This is what this does to give me a hint? What did I ever do to you?
Step Seven: Lets see if we can make the JS changes work with the JS Fiddle
Great odin... that HTML's 2000 lines long?
Okay, I'm working with pastebin here for the sake of saving space in the post here.
Alright, you started off with this: http://pastebin.com/xjmm4cev
You're using a lot of no-wrap, and putting classes onto individual elements. You shouldn't have to do this on each HTML element, CSS takes care of that very effectively, so lets go ahead and just rip out all the nowrap=nowrap and class="header" stuff (we'll put it back in a moment, but only ONE per group, not each element).
Then lets get rid of the useless blank lines.
Lets run this through HTML tidy and get it nice and indented correctly.
http://pastebin.com/uHtSZ4h5
Much easier to read over. Okay, so what do we see here? Well, it looks like you keep going in circles, cutting and pasting the same thing again and again. You also in-line javascript such as using onchange and onclick attributes. This is generally an awful thing to do to your code, and makes it hard to maintain (as I'm sure you've seen with this 2000+ line beast of cut+paste 27 times in a row).
So, lets take a look here:
elements, not inside a form
Elements in a table that are outside of rows, but not head/body/footer sections of a table
Code that's a huge pain to maintain because it keep going in circles, if you need to change this, you're basically screwed.
Lets fix all that.
We're going to use events in the tags, rather than in-line things. So, all of those in-line onchange and click attributes get the boot.
All of these inputs that are just floating around need to get put into a form, and taken out of this place in tables that only rows or table sections belong in.
Figure out how we can not have excessive input elements, if we can help it.
What the heck is the )="" that you have on every input button? Deleted.
So, here's all of your hidden boxes: http://pastebin.com/LXZSkvyf which I've removed, because we don't have a anywhere.
And here's what the code looks like without all of these weird things in it: http://pastebin.com/MiaJTGpb
Much more readable, but still not quite there.
Step 8: What can you do to make the HTML of the table work better?
You're using Thead and Tbody, and that's good.
You're using attributes for things like cellpadding, that's bad.
You've given each body row an ID. I don't feel you needed to do this, but it's not always bad. However, I'll show you how you could work without it.
You give some selects a select-box class, but it's nowhere in the css. I've removed it.
You give a title attribute to the selects that says "option_value". The title attribute is generally used to make a tool-tip popup when you leave the mouse over something. I'm not sure what you're going to do here, but that's bound to confuse your user. I'd highly suggest giving something better than option_value in that place.
You keep using the ID Submit_FMS_AddDelivery. HTML ID tags are meant to be unique, and used only once on one element. You've got it 27 times, that's bad. I don't think you need an ID on it, so I've removed it.
You've also got input-btn going on, I'm removing it, because you haven't shown it's used anywhere.
You probably don't need any of these ID tags on select and TR to be honest, so I'm pulling them.
What's that look like? Basically, you've got what's almost just the data, in it's nice pure form. That's good. http://pastebin.com/UNS6CAtb
Step 9: What were you trying to do?
Lets step back and take a look here.
All you really wanted to do was keep a fixed header, but you've ended up doing a lot of JavaScript hacks and manipulating the DOM in a lot of places. We need to stop doing this. Is there a simpler way to do what you want?
I'll have to expand on how to make this function with the rest of your stuff, but I get up for work in a few hours. I'll update again with more... But we'll get there.
Right now, because I haven't fixed what I took out, it doesn't look right. But I'm tired.
So, here's where we are so far: http://jsfiddle.net/5C6z7/
Plus those inputs we took out (and will be going back in later, in a different way)
Looks like all you have to do is to take the padding into account: each cell has 3 px padding both left and righ so you have to add 6 px to the width:
$t.find('tr:first th').each(function(){cols.push($(this).width()+6);});
Otherwise the cells with only one word inside will "push" the actual width a bit wider so that the word will fit and other cells with space to move will compensate by becoming a bit narrower. The header and the body both do this independently with different contents which creates the difference in actual cell widths.
EDIT: For Firefox you also need to widen the table so that the cells fit. After calculating the column widths add
var actualWidth = $t.width()+cols.length*6;
$t.width( actualWidth );
And later change the wrapper to:
$wrap.css({width:actualWidth,height:o.height,overflow:'auto'});
EDIT 2: To have both the header and the body scroll simultaneously you need to wrap them both to an outer div that handles the scrolling.
var $outerWrap = $( '<div>' ).css( {width:"300px", overflow:'auto' } );
var $wrap=$('<div>').css(
{ width:actualWidth,height:o.height,"overflow-y":'auto', "overflow-x":'hidden' }
);
$firstRow.wrap( $outerWrap );
$firstRow.after( $wrap );
$wrap.append( $t );
Demo: http://jsfiddle.net/YcRTz/2/
What about replacing '<th>' tags with '<td>' tags?
No extra js code required.
http://jsfiddle.net/spQAh/7/
Okay so instead of troubleshooting or using anything complex, I came up with simple CSS + JS solution to your problem. Take a look at http://jsfiddle.net/TdLQT/
Obviously, the process of making header static, can be made dynamic, meaning trigger it after user scrolls a bit or have it there by default or count other object's position from top window and trigger static behavior when it reaches or exceeds certain pixels. I can provide that if you give me details on exact design of your HTML page.
Anyways, I as you can see, have used fixed pixel heights which you can choose not to or make them elastic or dynamic. What is important to know is that, though solution relies on JS a bit, output position is purely from CSS. I am using classes to manipulate the position.
the simple solution is that when you calculate the width of the column you do not include padding. So you need to change the line
$t.find('tr:first th').each( function() {
cols.push($(this).width());
});
to
$t.find('tr:first th').each( function() {
cols.push($(this).outerWidth());
});
if your cells have margins use .outerWidth(true)
This works in quirks mode with IE7&8 and Chrome IE8 has a problem in strict mode. However I believe that that problem is caused by the added scrollbar. If you want to use IE8 in strict mode you have to allow for the width of the vertical scrollbar..

Categories