Understanding "this" reference in D3.js - javascript

I use this code:
d3.selectAll('rect')
.on('mousemove',function(){d3.select(this).attr("fill", "red");})
.on('mouseleave',function(){d3.select(this).attr("fill", "black");})
.on('click', function(){alert(this.tagName);})
;
d3.selectAll('text')
.on('mousemove',function(){d3.select(this.parentNode).select('rect').on('click').apply(this);})
;
.chart rect {
fill: steelblue;
}
.chart text {
fill: white;
font: 10px sans-serif;
text-anchor: end;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.0.4/d3.min.js"></script>
<svg class="chart" width="420" height="120">
<g transform="translate(0,0)">
<rect width="40" height="19"></rect>
<text x="37" y="9.5" dy=".35em">4</text>
</g>
<g transform="translate(0,20)">
<rect width="80" height="19"></rect>
<text x="77" y="9.5" dy=".35em">8</text>
</g>
<g transform="translate(0,40)">
<rect width="150" height="19"></rect>
<text x="147" y="9.5" dy=".35em">15</text>
</g>
<g transform="translate(0,60)">
<rect width="160" height="19"></rect>
<text x="157" y="9.5" dy=".35em">16</text>
</g>
<g transform="translate(0,80)">
<rect width="230" height="19"></rect>
<text x="227" y="9.5" dy=".35em">23</text>
</g>
<g transform="translate(0,100)">
<rect width="420" height="19"></rect>
<text x="417" y="9.5" dy=".35em">42</text>
</g>
</svg>
I expect that when hovering over text element will be triggered event for sibling rect elements and showing message "rect". But the message "text" showed, although code select('rect') must be set "this" referenced to rect element.
Why reference to text element is passed to handler?

The this you're passing to .apply() refers to the text element that the event was fired for, not for the rect element you've selected afterwards (selecting an element doesn't change this). You need to call the click handler in the new context:
d3.selectAll('text')
.on('mousemove',function(){
d3.select(this.parentNode).select('rect').each(function() {
d3.select(this).on("click").apply(this);
});
});
Complete demo here.

Related

text is going out of <svg><rect> box once in 1000x times but on safari browser always reproduce this bug

Im trying to fix this bug, and I have tried alot things but nothing help, if I give value of x='23%' like <text id="badgeText" x = "23% ... I can fix bug on safari but than bug start repodruce everytime on other browsers(mozila,chrome etc...), anyhow the bug is reproducing once in many times on other browsers ...
bug image
<div class="CoveoShowIfFieldValue" data-field="#isRecommended" data-field-value="True">
<svg height="25px" style="overflow:visible!important;">
<g id="recommendedBadge" style="object-fit:contain!important;">
<rect x="0" y="0" rx="15px" ry="15px"/>
<text id="badgeText" y="66%" font-family="Metric" text-anchor="middle" class="CoveoText" data-value="RECOMMENDED" style="text-transform: uppercase;"></text>
<g class="tooltip" transform="translate(21,62)">
<rect x="-6%" width="450" y="-35" height="25" rx="5px" ry="5px"/>
<text y="-31" dy="1em" font-family="Metric" text-anchor="middle" font-weight="100" class="CoveoText" data-value="RecommendedTooltip"></text>
</g>
</g>
</svg> </div>
It’s not a bug:
You need to add some width related properties like width or a viewBox.
Otherwise a browser can’t tell how to render a relative unit within a svg element.
Some browsers might be more forgiving by guessing or setting default width values as fallbacks. (Currently most firefox and chromium based browsers use 300px as a fallback).
<style>
svg {
border: 1px solid red;
height: 25px;
}
text {
fill: #fff;
}
</style>
<h2>Use viewBox</h2>
<svg viewBox="0 0 450 25" style="overflow:visible!important;">
<g id="recommendedBadge">
<rect x="0" y="0" rx="15" ry="15" width="100%" height="25" />
<text id="badgeText" x="50%" y="50%" dy="2" dominant-baseline="middle" font-family="Metric" text-anchor="middle" class="CoveoText" data-value="RECOMMENDED" style="text-transform: uppercase;">
Badge Text
</text>
<g class="tooltip" transform="translate(0 25)">
<rect x="0" y="0" width="100%" height="100%" rx="5" ry="5" fill="red" />
<text x="50%" y="50%" dy="5" font-family="Metric" text-anchor="middle" font-weight="100" class="CoveoText" data-value="RecommendedTooltip">tooltip</text>
</g>
</g>
</svg>
<br><br><br>
<h2>Use width</h2>
<svg height="25" width="100%" style="overflow:visible!important;">
<g id="recommendedBadge">
<rect x="0" y="0" rx="15" ry="15" width="100%" height="25" />
<text id="badgeText" x="50%" y="50%" dy="2" dominant-baseline="middle" font-family="Metric" text-anchor="middle" class="CoveoText" data-value="RECOMMENDED" style="text-transform: uppercase;">
Badge Text
</text>
<g class="tooltip" transform="translate(0 25)">
<rect x="0" y="0" width="100%" height="100%" rx="5" ry="5" fill="red" />
<text x="50%" y="50%" dy="5" font-family="Metric" text-anchor="middle" font-weight="100" class="CoveoText" data-value="RecommendedTooltip">tooltip</text>
</g>
</g>
</svg>

Set X and Y value for g element of SVG

I am relatively new in SVG drawing with HTML5.
What I want to do is to make a group of elements in SVG with g element so that all elements inside of that g element can work like a group and all the element's base x and y value can be received from the upper g element.
So, what I have done is something like this-
<svg width="500" height="300" xmlns="http://www.w3.org/2000/svg">
<g x="1000" y="1000">
<title>SVG Title Demo example</title>
<rect width="200" height="50"
style="fill:wheat; stroke:blue; stroke-width:1px"/>
<text style="text-anchor: middle;" class="small">My Text</text>
</g>
</svg>
What I expected is all the elements inside the g element will get x="1000" and y="1000" so my expected output is like this-
But I am getting this-
Re-
I don't like to set x and y element in text element. I just want to set relative x and y into the text element if needed, but that should be relative to g element.
Can anyone help me what I need to do to achieve my target with a group in SVG?
<g> elements don't support x or y attributes. You can use a transform instead though.
I've decreased the values from 1000 to 100 as otherwise the output is outside the 500 x 300 canvas of the outer <svg> element.
I've provided additional x and y attributes on the text element so it appears positioned as in your example. If wanted you could put the text itself in a <g> element or an <svg> element.
<svg width="500" height="300" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(100, 100)">
<title>SVG Title Demo example</title>
<rect width="200" height="50"
style="fill:wheat; stroke:blue; stroke-width:1px"/>
<text x="100" y="30" style="text-anchor: middle;" class="small">My Text</text>
</g>
</svg>
or using an additional <g> element to avoid x and y on the text itself.
<svg width="500" height="300" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(100, 100)">
<title>SVG Title Demo example</title>
<rect width="200" height="50"
style="fill:wheat; stroke:blue; stroke-width:1px"/>
<g transform="translate(100, 30)">
<text style="text-anchor: middle;" class="small">My Text</text>
</g>
</g>
</svg>
Alternatively you could use an inner <svg> element instead of a <g> element as that does support x and y attributes
<svg width="500" height="300" xmlns="http://www.w3.org/2000/svg">
<svg x="100" y="100">
<title>SVG Title Demo example</title>
<rect width="200" height="50"
style="fill:wheat; stroke:blue; stroke-width:1px"/>
<text x="100" y="30" style="text-anchor: middle;" class="small">My Text</text>
</svg>
</svg>

Whats the correct way to get the bounding box of DOM elements containing svg

I have the following html and get strange results with getBoundingClientRect, if there are svg elements inside:
<html>
<head>
<meta charset="utf-8">
<title>Test</title>
</head>
<body>
<svg>
<g transform="translate(10,10) scale(1)">
<g class="nodes">
<g style="inline" transform="translate(20,20)">
<rect style="stroke: rgb(170, 170, 170); stroke-width: 1; fill: rgb(248, 248, 248);" width="100" height="90"></rect>
<g class="nodeparent">
<rect class="noderect" style="fill: none; stroke: rgb(182, 204, 216); stroke-width: 0;" x="0" y="0" height="20" width="100"></rect>
<text class="nodetext" x="3" y="15">Text 1</text>
</g>
<g class="nodeparent">
<rect class="noderect" style="fill: none; stroke: rgb(221, 185, 172); stroke-width: 0;" x="0" y="22" height="20" width="100"></rect>
<text class="nodetext" x="3" y="37">Test 2</text>
</g>
<g class="nodeparent">
<rect class="noderect" style="fill: none; stroke: rgb(221, 185, 180); stroke-width: 0;" x="0" y="44" height="20" width="100"></rect>
<text class="nodetext" x="3" y="59">Test 3</text>
</g>
<g class="nodebox">
<rect class="noderect" style="fill: rgb(236, 163, 154); stroke: rgb(212, 139, 130); stroke-width: 2;" x="0" y="66" height="20" width="100"></rect>
<text class="nodetext" x="3" y="81">Test 4</text>
<g class="nodeicon">
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" x="80" y="68">
<rect height="14" width="14" y="1" x="1" stroke="#888888" stroke-width="1" fill="#ffffff" fill-opacity="0.5"></rect>
<line x1="4" y1="8" x2="12" y2="8" stroke="#888888" stroke-width="2"></line>
</svg>
</g>
</g>
</g>
</g>
</g>
</svg>
</body>
</html>
I get a much greater rectangle than I would expect in Firefox.
When I inspect the objects, the displayed bounding box for the inner svg element is fine, but the surrounding g element (class nodeicon) is outside.
If I remove this g element, the next surrounding g element is outside.
The following picture shows this:
It looks like the offset of the svg is applied twice.
Is getBoundingClientRect the correct way to get the position and size of elements (e.g. the g element with class nodes) for this? Is there something wrong with the HTML or svg element or did I run into a Firefox bug?
I am using the current version of Firefox (58.0 64bit).
A problem that you have here is that the svg tag nested inside the g tag (.nodeicon) is starting a new viewport context. Strictly speaking it should not be nested inside a g tag anyway, but regardless, it isn't really necessary as you're using it as a method of grouping the two elements inside it - which is the purpose of the g tag.
Try removing the svg tag nested inside .nodeicon, and move the coordinates in that svg's x and y attributes to a transform attribute on the g tag.
i.e:
<g class="nodeicon" transform="translate(80, 68)">
<rect height="14" width="14" y="1" x="1" stroke="#888888" stroke-width="1" fill="#ffffff" fill-opacity="0.5"></rect>
<line x1="4" y1="8" x2="12" y2="8" stroke="#888888" stroke-width="2"></line>
</g>

How i can style SVG <use> elements on hover?

I'm beginner in SVG. I'm trying to change style of multiple <use> elements on hover at a specific <use> element with css, but I can't, because <use> elements using Shadow DOM.
I have the following <defs>:
<defs>
<filter id="Sjax0b81q1" filterUnits="userSpaceOnUse">...</filter>
<circle cx="0" cy="0" r="40" id="action-circle" style="cursor: move; fill: #fff;" filter="url('#Sjax0b81q1')" class="el action-el"></circle>
<g id="condition-rhombus" style="cursor: move; fill: #fff;" class="el condition-el" transform="matrix(1,0,0,1,0,0)">
<circle cx="0" cy="0" r="40" fill="none"></circle>
<path d="M -35 0, L 0 -35, L 35 0, L 0 35 L -35 0" style="stroke-linecap: round; stroke: white;" filter="url('#Sjax0b81q1')" class="condition-rhombus"></path>
</g>
<g id="svg-plus-button">
<circle cx="0" cy="40" r="10" id="svg-plus-circle" fill="none" style="fill-opacity: 1;" class="svg-plus-circle"></circle>
<path d="M956.8,408.....408.5z" id="svg-plus-sign" fill="none" transform="matrix(0.008,0,0,0.008,-4,36)" style="pointer-events: none;" class="svg-plus-sign"></path>
</g>
<rect x="-20" y="-20" width="40" height="40" id="rect-active-layer" fill="none" style="pointer-events: visible;" class="rect-active-layer"></rect>
<path d="M252.967.....2v1Z" id="api-svg" class="cls-1"></path>
</defs>
And I have a group of elements that contains several <use> elements:
<g id="action-group-2" class="external action-group" transform="matrix(1,0,0,1,420,180)">
<g class="internal action-group">
<rect x="-40" y="-40" width="80" height="80" fill="none"></rect>
</g>
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#action-circle"></use>
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#svg-plus-button" id="useSjax0b81q1k"></use>
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#rect-active-layer"></use>
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#api-svg"></use>
</g>
For example, I need to change the <path> fill in element with id #api-svg, when I hover on the #action-circle.
How can I do this? Maybe there is another way to render and styling reusable elements on hover.
Define the path to have fill="inherit", then you should be able to set fill="whatever" on the <use> element's styles and it will work.
use:hover {
fill: red;
}
<svg>
<defs>
<circle id="test" fill="inherit" cy="10" r="10" />
</defs>
<use xlink:href="#test" transform="translate(10,0)" />
<use xlink:href="#test" transform="translate(30,0)" />
<use xlink:href="#test" transform="translate(50,0)" />
<text y="40">Hover over the circles!</text>
</svg>
There's nothing overly complex here. Just change the 'use' element, rather than anything in the defs area (unless you want it to affect everything that references it).
You can either style the use element via normal css, via a css selector, e.g it's id is possibly simplest.
Or you can set the fill svg attribute on the svg 'use' element you are handling.
You shouldn't need fill inherit or anything unless I'm missing something.

SVG text and image centered within a rectangle

I'm really not familiar with SVG's so I'm sorry if this is actually a fairly easy problem..
I'm creating an SVG:
<svg height="100%" width="100%">
<rect x="0" y="0" width="100%" height="100%" fill="#da552f"></rect>
<image xlink:href="https://d30y9cdsu7xlg0.cloudfront.net/noun-svg/703414.svg?Expires=1481685113&Signature=hsa76aA6t5W6xisN8bYKk5t74cmOzTXmYUObaVwE0hUso99Gb4czprrsQAtkaC0aOQJBhNfAn8MjRpKyu8M~AzS5OS3rthGOLFqa3Pk2lCwAWjs-KtTa9fSo7w-sZSJwG6LDeRm5B6T5hYnoKQLibJzCtHvSdUYlp5XBUx1RNvs_&Key-Pair-Id=APKAI5ZVHAXN65CHVU2Q" transform="translate(-35.5,-31)" x="50%" y="50%" height="50px" width="50px"/>
<text fill="#ffffff" x="50%" y="50%" alignment-baseline="middle" text-anchor="middle" font-size="48" font-family="Verdana">Kitty Cat</text>
</svg>
And as you can see both the image of the cat and the text are centered in the rectangle, but this isn't the desired effect I want.
I'd like the image to be next to the text and both of them be centered in the rectangle.. example:
How is this doable using SVGs? Is javascript required? Any help would be great! Thanks
Unlike HTML, SVG has no automatic layout of groups of elements.
You have two choices:
Cheat and put your image and text in HTML and use a <foreignObject> element to embed the HTML in your SVG. Although it is barely an SVG any more. And that only works in browsers.
Use Javascript to measure the text and then re-position it in the centre.
function reCentre() {
var svg = document.getElementById("mysvg");
var group = document.getElementById("centreMe");
// Get the bounding box of the image+text group
var groupBounds = group.getBBox();
// Get the size of the SVG on the page
var svgBounds = svg.getBoundingClientRect();
// Calculate new position for the group
var groupPosX = (svgBounds.width - groupBounds.width) / 2;
var groupPosY = (svgBounds.height - groupBounds.height) / 2;
// Calculate the difference between the groups current position
// and where it needs to be in order to be centred.
var dx = groupPosX - groupBounds.x;
var dy = groupPosY - groupBounds.y;
// Give the group a translate transform to move it to this new position
group.setAttribute("transform", "translate("+dx+","+dy+")");
}
// Initial centering
reCentre();
// Also recentre when window resizes
window.addEventListener("resize", reCentre);
<svg id="mysvg" height="100%" width="100%">
<rect x="0" y="0" width="100%" height="100%" fill="#da552f"></rect>
<g id="centreMe">
<image xlink:href="https://d30y9cdsu7xlg0.cloudfront.net/noun-svg/703414.svg?Expires=1481685113&Signature=hsa76aA6t5W6xisN8bYKk5t74cmOzTXmYUObaVwE0hUso99Gb4czprrsQAtkaC0aOQJBhNfAn8MjRpKyu8M~AzS5OS3rthGOLFqa3Pk2lCwAWjs-KtTa9fSo7w-sZSJwG6LDeRm5B6T5hYnoKQLibJzCtHvSdUYlp5XBUx1RNvs_&Key-Pair-Id=APKAI5ZVHAXN65CHVU2Q" x="0" y="-50" height="50px" width="50px"/>
<text fill="#ffffff" x="80" y="0" font-size="48" font-family="Verdana">Kitty Cat</text>
</g>
</svg>
Edit the x and y attributes of the image tag until the cat face is where you would like it.
<svg height="100%" width="100%">
<rect x="0" y="0" width="100%" height="100%" fill="#da552f"></rect>
<image xlink:href="https://d30y9cdsu7xlg0.cloudfront.net/noun-svg/703414.svg?Expires=1481685113&Signature=hsa76aA6t5W6xisN8bYKk5t74cmOzTXmYUObaVwE0hUso99Gb4czprrsQAtkaC0aOQJBhNfAn8MjRpKyu8M~AzS5OS3rthGOLFqa3Pk2lCwAWjs-KtTa9fSo7w-sZSJwG6LDeRm5B6T5hYnoKQLibJzCtHvSdUYlp5XBUx1RNvs_&Key-Pair-Id=APKAI5ZVHAXN65CHVU2Q" transform="translate(-35.5,-31)" x="25%" y="45%" height="50px" width="50px"/>
<text fill="#ffffff" x="50%" y="50%" alignment-baseline="middle" text-anchor="middle" font-size="48" font-family="Verdana">Kitty Cat</text>
</svg>
you could use a webfont or an emoji instead of an image...
svg {
position: absolute;
top: 0px;
left: 0px;
bottom: 0px;
right: 0px
}
<svg viewBox="0 0 400 200">
<rect x="0" y="0" width="400" height="200" fill="pink" />
<text x="200" y="100" dominant-baseline="middle" text-anchor="middle" font-size="30" font-family="sans serif">
<tspan font-size="50">🐱</tspan>Kitty Cat</text>
</svg>

Categories

Resources