I have been experimenting with the use tag within an svg tag. It seems relatively simple and I have a basic example working in JSFiddle: https://jsfiddle.net/cneLLLqu/
Clicking on the red square (outside of the defs and use tags) yields a message box. Clicking on the green square does not fire any click event. I don't understand why events fire with the red square but not with the green. Inspecting the element everything looks fine.
I am currently using Opera 42.
What you want to do is not possible. From the SVG 1.1 spec:
The effect of a ‘use’ element is as if the contents of the referenced
element were deeply cloned into a separate non-exposed DOM tree which
had the ‘use’ element as its parent and all of the ‘use’ element's
ancestors as its higher-level ancestors. Because the cloned DOM tree
is non-exposed, the SVG Document Object Model (DOM) only contains the
‘use’ element and its attributes. The SVG DOM does not show the
referenced element's contents as children of ‘use’ element.
They must be completely independent and you don't have to use <use>.
<svg xmlns="http://www.w3.org/2000/svg">
<rect id="rect-1" fill="#D0011B" x="0" y="0" width="50" height="50" onclick="alert('rect-1 clicked!')"></rect>
<rect id="rect-2" fill="#4990E2" x="70" y="0" width="50" height="50" onclick="alert('rect-2 clicked!')"></rect>
</svg>
I just saw the comment you made in the other answer and in that case you have to add the onclick handler to the <use> tag not the defs element.
<use xlink:href="#test" x="60" y="0" onclick="alert('clicked')">
Related
I've made a cut down version of the issue I'm having in the JSFiddle here.
I use d3.js to dynamically add SVG path elements with class symbol to the group with id grouped-shapes in the following defs tag:
<defs>
<g id="grouped-shapes">
...
</g>
</defs>
I then use this definition twice, once directly and once via a reference to the first use tag:
<g id="first-triangle">
<use xlink:href="#grouped-shapes" transform="translate(100, 100)"/>
</g>
<g id="second-triangle">
<use xlink:href="#first-triangle" transform="translate(200, 200)
rotate(30)"/>
</g>
(In this example case I could easily avoid the problem by working out everything as a transformation of the original grouped-shapes instead of doing this indirect transformation, but in my real use case this would be more complicated.)
I then set up an event listener to remove paths with class symbol on clicking the container div:
document.getElementById("kaleidoscope-container")
.addEventListener("click", function( event ) {
d3.selectAll(".symbol").remove();
}, false);
In Chrome, both groups update on clicking, but in Firefox only the first one does.
Which of these behaviours is correct? I'm looking to be able to update in both instances, as in Chrome - what is the correct way of going about this?
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).
I have some SVG code embedded in HTML and I need to add several polygons to the existing SVG with javascript. The problem is that the SVG that exists (that I am adding to) is in a group (g tag) and because of layering, I need to add the new polygons to specific places in that group. I've been writing javascript for a few years now, but I am just starting to learn SVG and I don't know how to add elements to a specific place in the SVG DOM with javascript. My comment below explains why this is different than a previously asked question.
For example:
<g id="group1">
<rect x="20" y="50" width="700" height="20" fill="url(#grad1)"/>
I need to add polygons here.
<rect x="20" y="140" width="700" height="20" fill="url(#grad1)"/>
</g>
Use Node.insertBefore:
var insertedNode = parentNode.insertBefore(newNode, referenceNode);
If referenceNode is null, the newNode is inserted at the end of the list of child nodes.
I have the following code:
<div class="blah" style="clip-path: url(#clippath)"></div>
<svg width="0" height="0"><defs><clipPath id="clippath">
<rect x="0" y="0" height="100" width="100"></rect>
</clipPath></defs></svg>
This correctly clips the blah div to 100x100 square. However, if I use JavaScript to add the svg to the DOM (rather than it being there from page load), it no longer works. Specifically, I'd like to create a dynamic clip path based on events happening in my app.
What am I doing wrong? This only needs to work in Firefox (which unfortunately doesn't support clip-path: polygon(...))
Make sure you are you using the namespace variant of createElement.
document.createElementNS("http://www.w3.org/2000/svg", "clipPath");
If you are just using createElement() then the element will be going into the default namespace (ie HTML) and won't be recognised by the SVG renderer.
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