Mouseover Hiding Stacked Elements From Mouse (HitTesting) - javascript

I'm having a little trouble working out if this is even possible with D3 let alone how to do it.
I have a rect elements that I have attached 3 event handlers to ("click", "Mouseover", "mouseout"). This is all working great the only trouble is that I also add a text element to the the same <g> that is placed on top of the rect element. like this:
<g>
<rect x="204" y="204" width="204" height="204" style="fill: yellow; pointer-events: all; fill-opacity: 0.402592;" stroke="none" stroke-width="0" stroke-dasharray="15,15,15">
<text class="word" x="306" y="306" text-anchor="middle" fill="black" style="fill-opacity: 1.00648;">Text Element</text>
</g>
The problem I'm seeing is that the mouseout event is fired when the mouse is still over the rect but the text element is immediately below the mouse and basically "inbetween" the mouse and the rect below the text.
Is there any way to make the text "invisible" to the mouse?
I guess if we were talking WinForms/WPF the analgulous feature I'm looking for in D3 is something along the lines of IsHitTestable=false.

Typical isn't it ... just as I ask the question the solution presents itself.
In order to hide stacked elements from mouse events add this style declaration:
.style("pointer-events", "none")

Related

Overflow SVG does not register clicks on OSX/iOS

I have a small SVG canvas in my application which houses a button to click. Upon clicking that button, the small circle does some flashy work and opens up to an appropriate sized bubble to fit all the data. This data is just a list of data with custom bullets off to the left; some of this text is clickable (bound to click events) and causes other functions to be fired off. My problem is that the SVG canvas never changes in size. It's really only big enough to contain the original circle that the user clicks on. The rest of SVG elements that make up this control flow outside the bounds of the SVG canvas, but is all viewable due to the SVG having its overflow set to visible. Everything works fine in all browsers, but things change when I open up the application in an iPad or on a Mac (regardless of browser). It seems the overflow pieces of my SVG are completely invisible to the browser. Any click event that fires fine on the overflowed SVG elements on Window's browsers are completely invisible on OSX/iOS browsers (it seems like any click actually falls through to whatever is behind the overflowed SVG). At first I thought it was an issue with the click binding, but after searching and playing around with it, I think OSX/iOS just doesn't handle overflowed SVG very well.
To explain in its simplest form what's going on in my application, I have a very simple JSFiddle here: https://jsfiddle.net/yno8daka/
HTML:
<svg style="overflow: visible !important" class="canvas" width='70px' height='70px' xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
<circle id="circle1" stroke="black" fill="black" stroke-width="1" cx="10" cy="10" r="10"></circle>
<circle id="circle2" stroke="black" fill="black" stroke-width="1" cx="110" cy="110" r="10"></circle>
</svg>
Javascript:
$('#circle1').click(function() {
alert('yeah');
})
$('#circle2').click(function() {
alert('yeah2');
})
Circle1 is in the bounds of the SVG canvas, while circle2 is not. On Windows browsers, you will be able to click on both circles without any issue. On OSX/iOS browsers, the click event will fire for circle1, but not for circle2.
Does anyone know how to get this to work?

How to manipulate the layer of a element in an existing SVG?

I'm using d3 to manipulate an existing svg. The svg appears to have multiple layers. I'm able to get a handle to an expected element and manipulate it with d3. However, the element is on a lower layer. For example, I can set stroke (border color) and stroke-width on the element through d3 and I can see the updated border expanding out from beneath a higher layer with the same shape.
I need to figure out how to dynamically change element layers as needed on the fly. I tried setting z-index style and attr to 999 for the layer I'm trying to raise. No other z-index attrs exist in the svg so my assumption was that setting an element z-index to 999 would most likely raise it to the top but this did not happen. This assumption was mainly based on my background in html/css.
Can you recommend some basic troubleshooting steps for this? Is svg layering implementation and manipulation more complex than what I have in mind? Can you recommend any resources or possible shortcuts?
There is no z-index in a SVG. In an SVG, the order of the elements defines the order of the "painting", and the order of the painting defines who goes on top. The specs are clear:
Elements in an SVG document fragment have an implicit drawing order, with the first elements in the SVG document fragment getting "painted" first. Subsequent elements are painted on top of previously painted elements.
Therefore, you'll have to reposition the elements. There is a very simple solution, just do:
selection.raise();
Raise re-inserts each selected element, in order, as the last child of its parent.
Here is a demo, hover over the circle to bring it to the top:
d3.selectAll("circle").on("mouseover", function(){
d3.select(this).raise()
});
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="400" height=200>
<circle cy="100" cx="80" r="60" fill="blue"></circle>
<circle cy="100" cx="160" r="60" fill="yellow"></circle>
<circle cy="100" cx="240" r="60" fill="red"></circle>
<circle cy="100" cx="320" r="60" fill="green"></circle>
</svg>
Note that raise() will only work for elements in the same level (that is, having the same parent element).

hide nodes and children in D3.js using JavaScript or jQuery

I´m forking an excellent force directed layout (#eyaler http://bl.ocks.org/eyaler/1058611) with multiple options, and I would like to hide a specific node with children using jQuery or D3+JavaScript, not directly from d3 code using the toggle function. (this is important because I have an external button in the html code)
http://bl.ocks.org/carlos-andres/c3194c284763fde317b0
I tried commands like:
d3.selectAll("g#Avenger").attr("visibility", "hidden");
d3.select("g#Spathi").selectAll("*").attr("visibility", "hidden");
d3.select("g#Spathi").selectAll(this.children).attr("visibility", "hidden");
d3.select("g#Spathi + g").each(function(d){ console.log(d)});
It hides the node, but not the label and path. I also tried:
jQuery('g#Avenger').siblings().toggle();
It hides all the other nodes.
UPDATE: I tried to use the solution here: A d3.select... equivalent to jQuery.children() given from #Klaujesi and it doesn't work for me. I'm also checked another post, How to display and hide links and nodes when clicking on a node in D3 Javascript and I can´t get good results with that approach either.
Check the image; it hides node g#Spathi but not the children and paths
You has <tex> outside <g> element:
<g id="Drone" class="node" ...
<path d="M-8.378872.....city: 1;"></path>
</g>
<text dy=".35em" dx="9.45 .... city: 1;"> Drone</text>
must be:
<g id="Drone" class="node" ...
<path d="M-8.378872.....city: 1;"></path>
<text dy=".35em" dx="9.45 .... city: 1;"> Drone</text>
</g>
make <text>, <g> dependant

Adding a button to a <g> element using d3

I was wondering what was the best way to go about adding a button to my group elements using d3.
I have currently built my visual using d3 to look something like:
<div>
<svg>
<g>
<rect>
</g>
<g>...</g>
</svg>
</div>
So I am wondering how I would add a clickable button in each group element. Some ideas I had were:
g.append("a")
.append("image")
.attr("src", ...);
or
g.append("a")
.append("rect")
.attr(...)
Ultimately I would like the button to perform an onclick event. What is the best way to go about this?
You can create a rect and use d3's selection.on api to register a click listener:
var rect = d3.select('svg g rect');
rect.on('click', function() {
console.log('i was clicked');
});
.as-console-wrapper { max-height: 3.5em !important; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg width="400" height="400">
<g transform="translate(50,50)">
<rect height="100" width="200" fill="red"/>
</g>
</svg>
Button isn't an SVG component, you can draw a rect or a circle(usually called legend) instead, and binding the click event on the legend.
You can refer to grouped bars with legend click event

How to make a g element in an SVG clickable

I understand that it is a grouping for transformations. I have also looked through the documentation and have found nothing regarding this, but was wondering whether it is possible.
Fill it with a <rect width="whatever the <g> width is" height="whatever the <g> height is"/> and make the <rect> clickable. The rect can be transparent check out the pointer-events property for how to configure clicability of the <rect>.
You can find the <g> height and width by calling getBBox.
Assign id to element, and listener.
Ex.
<g id="clickg"><circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/><g>
$("#clickg").on("click",function () {
alert("click");
});
Super old question but it helped me out so I want to expand on one of the answers with a little more of a step-by-step guide and some more details. This is the method I made when my group was several disconnected items and I wanted the whole area including the blank areas in between to be clickable.
Get the x/y/width/height of the <g> element, you can do this easily in chrome dev tools by selecting the <g> element and entering $0.getBBox() in the console.
Create a new element as a child of your <g> like so: <rect x="TODO" y="TODO" width="TODO" height="TODO" style="pointer-events: all" />
Replace the TODO above with the info from getBBox

Categories