I have the following code.
On click of a button it adds a circle , on hovering the circle a red square is shown and on mouseout it will be hidden. For one circle it works perfectly. But since i'm using d3.selectAll , when there are multiple circle it shows and hides all red rectangles when hovered on one circle.
Is there a way to select the rectangle associated to circle that is hovered using the d3.select or d3.selectAll ?
For demonstrating the problem in the code i've added 3 circles can be properly added
$(document).ready(function() {
var width = 560,
height = 500;
var i = -1;
valArray = [100, 200, 300, 400];
var svg = d3.select('#canvas')
.append('svg')
.attr('width', width)
.attr('height', height);
$("#add").click(function() {
i++;
var g = svg.append('svg:g')
.attr('x', valArray[i])
.attr('y', valArray[i]);
var yesDecision = g.append('svg:rect')
.style('fill', "#D64541")
.attr("width", "50")
.attr("height", "50")
.attr("id", "yesDecision")
.attr("class", "hoverNode")
.attr("x", valArray[i])
.attr("y", valArray[i]);
g.append('svg:text')
.attr('x', valArray[i])
.attr('y', valArray[i])
.attr('class', 'id hoverNode')
.text(function(d) {
return "Yes";
});
g.append('svg:circle')
.attr('class', 'node')
.attr('r', 40)
.attr('cx', valArray[i])
.attr('cy', valArray[i])
.style('fill', function(d) {
return ("#ccc");
}).on('mouseover', function(d) {
d3.selectAll(".hoverNode").style("visibility", "visible")
})
.on('mouseout', function(d) {
d3.selectAll(".hoverNode").style("visibility", "hidden")
})
});
});
.hoverNode {
visibility: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="add">Add element</button>
<div id="canvas">
</div>
One solution is setting a specific class for each rectangle, based on i:
var yesDecision = g.append('rect')
.attr("class", "hoverNode" + i)
And do the same for the circles:
g.append('circle')
.attr('class', 'node' + i)
Then, inside your mouseover and mousemove, you select the rectangle based on the circle class:
.on('mouseover', function(d) {
var elementID = d3.select(this).attr("class").slice(-1);
d3.selectAll(".hoverNode" + elementID).style("visibility", "visible")
})
.on('mouseout', function(d) {
var elementID = d3.select(this).attr("class").slice(-1);
d3.selectAll(".hoverNode" + elementID).style("visibility", "hidden")
})
Here is the working code (click "run code snippet"):
var width = 560,
height = 500;
var i = -1;
valArray = [50, 120, 190, 260];
var svg = d3.select("#chart")
.append("svg")
.attr('width', width)
.attr('height', height);
d3.select("#add").on("click", function() {
i++;
var g = svg.append('g')
.attr('x', valArray[i])
.attr('y', valArray[i]);
var yesDecision = g.append('rect')
.style('fill', "#D64541")
.attr("width", "50")
.attr("height", "50")
.attr("id", "yesDecision")
.style("visibility", "hidden")
.attr("class", "hoverNode" + i)
.attr("x", valArray[i])
.attr("y", valArray[i]);
g.append('circle')
.attr('class', 'node' + i)
.attr('r', 40)
.attr('cx', valArray[i])
.attr('cy', valArray[i])
.style('fill', function(d) {
return ("#ccc");
}).on('mouseover', function(d) {
var elementID = d3.select(this).attr("class").slice(-1);
d3.selectAll(".hoverNode" + elementID).style("visibility", "visible")
})
.on('mouseout', function(d) {
var elementID = d3.select(this).attr("class").slice(-1);
d3.selectAll(".hoverNode" + elementID).style("visibility", "hidden")
})
g.append('text')
.attr("x", valArray[i])
.attr("y", valArray[i])
.style("visibility", "hidden")
.attr("pointer-events", "none")
.attr('class', 'id hoverNode' + i)
.text("Yes");
});
<script src="https://d3js.org/d3.v4.min.js"></script>
<button id="add">Add</button>
<div id="chart"></div>
You could bind rect as data to circle and then get access to rect in mouse events:
g.append('svg:circle')
.data([yesDecision])
.attr('class', 'node')
.attr('r', 40)
.attr('cx', valArray[i])
.attr('cy', valArray[i])
.style('fill', function(d) {
return ("#ccc");
}).on('mouseover', function(d) {
d.style("visibility", "visible")
})
.on('mouseout', function(d) {
d.style("visibility", "hidden")
})
Here is your edited code:
$(document).ready(function() {
var width = 560,
height = 500;
var i = -1;
valArray = [100, 200, 300, 400];
var svg = d3.select('#canvas')
.append('svg')
.attr('width', width)
.attr('height', height);
$("#add").click(function() {
i++;
var g = svg.append('svg:g')
.attr('x', valArray[i])
.attr('y', valArray[i]);
var yesDecision = g.append('svg:rect')
.style('fill', "#D64541")
.attr("width", "50")
.attr("height", "50")
.attr("id", "yesDecision")
.attr("class", "hoverNode")
.attr("x", valArray[i])
.attr("y", valArray[i]);
g.append('svg:text')
.attr('x', valArray[i])
.attr('y', valArray[i])
.attr('class', 'id hoverNode')
.text(function(d) {
return "Yes";
});
g.append('svg:circle')
.data([yesDecision])
.attr('class', 'node')
.attr('r', 40)
.attr('cx', valArray[i])
.attr('cy', valArray[i])
.style('fill', function(d) {
return ("#ccc");
}).on('mouseover', function(d) {
d.style("visibility", "visible")
})
.on('mouseout', function(d) {
d.style("visibility", "hidden")
})
});
});
.hoverNode {
visibility: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="add">Add element</button>
<div id="canvas">
</div>
Related
Consider the code for some text I'm putting in some circles:
var margins = {top:20, bottom:300, left:30, right:100};
var height = 600;
var width = 1080;
var totalWidth = width+margins.left+margins.right;
var totalHeight = height+margins.top+margins.bottom;
var svg = d3.select('body')
.append('svg')
.attr('width', totalWidth)
.attr('height', totalHeight);
var graphGroup = svg.append('g')
.attr('transform', "translate("+margins.left+","+margins.top+")");
//var tsvData = d3.tsv('circle-pack-data.tsv');
//tsvData.then(function(rawData) {
/*
var data = rawData.map(function(d) {
return {id:d.id, parentId:d.parentId, size:+d.size}
});
*/
var data = [
[
{'id':'3Q18'},
{'id':'Greater China','parentId':'3Q18','size':428.7,'sign':'negative'},
{'id':'Thematic','parentId':'3Q18', 'size':111.8,'sign':'positive'},
{'id':'US','parentId':'3Q18', 'size':418.3,'sign':'positive'},
{'id':'India','parentId':'3Q18', 'size':9.39,'sign':'negative'},
{'id':'Europe','parentId':'3Q18', 'size':94.0,'sign':'positive'},
{'id':'Japan','parentId':'3Q18', 'size':0,'sign':'positive'},
{'id':'Global','parentId':'3Q18', 'size':41.9,'sign':'negative'}
],
[
{'id':'4Q18'},
{'id':'Greater China','parentId':'4Q18','size':217.8,'sign':'negative'},
{'id':'Thematic','parentId':'4Q18', 'size':100.5,'sign':'positive'},
{'id':'US','parentId':'4Q18', 'size':127.9,'sign':'negative'},
{'id':'India','parentId':'4Q18', 'size':1.5,'sign':'negative'},
{'id':'Europe','parentId':'4Q18', 'size':1.5,'sign':'positive'},
{'id':'Japan','parentId':'4Q18', 'size':0,'sign':'positive'},
{'id':'Global','parentId':'4Q18', 'size':52.8,'sign':'negative'}
],
];
var colorMap = {
'3Q18':"#d9d9d9",
'4Q18':"#d9d9d9",
'3Q19':"#d9d9d9",
'4Q19':"#d9d9d9",
'Greater China':"#003366",
'Thematic':"#366092",
'US':"#4f81b9",
'India':"#95b3d7",
'Europe':"#b8cce4",
'Japan':"#e7eef8",
'Global':"#a6a6a6"
};
var defs = svg.append('svg:defs');
var blue1x = "Fills/blue-1-crosshatch-redo.svg";
var blue2x = "Fills/blue-2-crosshatch.svg";
var blue3x = "Fills/blue-3-crosshatch.svg";
var blue4x = "Fills/blue-4-crosshatch.svg";
var blue5x = "Fills/blue-5-crosshatch.svg";
var blue6x = "Fills/blue-6-crosshatch.svg";
var grayx = "Fills/gray-2-crosshatch.svg";
defs.append("svg:pattern")
.attr("id", "blue1_hatch")
.attr("width", 20)
.attr("height", 20)
.attr("patternUnits", "userSpaceOnUse")
.append("svg:image")
.attr("xlink:href", blue1x)
.attr("width", 20)
.attr("height", 20)
.attr("x", 0)
.attr("y", 0);
defs.append("svg:pattern")
.attr("id", "blue2_hatch")
.attr("width", 20)
.attr("height", 20)
.attr("patternUnits", "userSpaceOnUse")
.append("svg:image")
.attr("xlink:href", blue2x)
.attr("width", 20)
.attr("height", 20)
.attr("x", 0)
.attr("y", 0);
defs.append("svg:pattern")
.attr("id", "blue3_hatch")
.attr("width", 20)
.attr("height", 20)
.attr("patternUnits", "userSpaceOnUse")
.append("svg:image")
.attr("xlink:href", blue3x)
.attr("width", 20)
.attr("height", 20)
.attr("x", 0)
.attr("y", 0);
defs.append("svg:pattern")
.attr("id", "blue4_hatch")
.attr("width", 20)
.attr("height", 20)
.attr("patternUnits", "userSpaceOnUse")
.append("svg:image")
.attr("xlink:href", blue4x)
.attr("width", 20)
.attr("height", 20)
.attr("x", 0)
.attr("y", 0);
defs.append("svg:pattern")
.attr("id", "blue5_hatch")
.attr("width", 20)
.attr("height", 20)
.attr("patternUnits", "userSpaceOnUse")
.append("svg:image")
.attr("xlink:href", blue5x)
.attr("width", 20)
.attr("height", 20)
.attr("x", 0)
.attr("y", 0);
defs.append("svg:pattern")
.attr("id", "blue6_hatch")
.attr("width", 20)
.attr("height", 20)
.attr("patternUnits", "userSpaceOnUse")
.append("svg:image")
.attr("xlink:href", blue6x)
.attr("width", 20)
.attr("height", 20)
.attr("x", 0)
.attr("y", 0);
defs.append("svg:pattern")
.attr("id", "gray_hatch")
.attr("width", 20)
.attr("height", 20)
.attr("patternUnits", "userSpaceOnUse")
.append("svg:image")
.attr("xlink:href", grayx)
.attr("width", 20)
.attr("height", 20)
.attr("x", 0)
.attr("y", 0);
var negativeMap = {
'3Q18':"#d9d9d9",
'4Q18':"#d9d9d9",
'3Q19':"#d9d9d9",
'4Q19':"#d9d9d9",
'Greater China':"url(#blue1_hatch)",
'Thematic':"url(#blue2_hatch)",
'US':"url(#blue3_hatch)",
'India':"url(#blue4_hatch)",
'Europe':"url(#blue5_hatch)",
'Japan':"url(#blue6_hatch)",
'Global':"url(#gray_hatch)"
};
var strokeMap = {
"Greater China":"#fff",
"Thematic":"#fff",
"US":"#fff",
'India':"#000",
'Europe':"#000",
'Japan':"#000",
'Global':"#000"
};
for (var j=0; j <(data.length); j++) {
var vData = d3.stratify()(data[j]);
var vLayout = d3.pack().size([250, 250]);
var vRoot = d3.hierarchy(vData).sum(function (d) { return d.data.size; });
var vNodes = vRoot.descendants();
vLayout(vRoot);
var thisClass = "circ"+String(j);
var vSlices = graphGroup.selectAll('.'+thisClass).data(vNodes).attr('class',thisClass).enter().append('g');
console.log(vNodes)
vSlices.append('circle')
.attr('cx', function(d, i) {
return d.x+(j*300)
})
.attr('cy', function (d) { return d.y; })
.attr('r', function (d) { return d.r; })
.style('fill', function(d) {
//console.log(d.data.id)
if (d.data.data.sign=='positive') {
return colorMap[d.data.id];
} else {
return negativeMap[d.data.id];
}
});
vSlices.append('text')
.attr('x', function(d,i) {return d.x+(j*300)})
.attr('y', function(d) {return d.y+5})
.attr('text-anchor','middle')
.style('fill', function(d) {return strokeMap[d.data.id]})
.style('background-color', function(d) {return colorMap[d.data.id]})
.text(function(d) {return d.data.data.size ? d.data.data.size : null});
}
<script src="https://d3js.org/d3.v5.min.js"></script>
Since the fill of the circle can sometimes be a crosshatch (not a solid color), the readability of the text takes a hit. I also found that simply adding a text shadow wasn't quite enough. I'd like to go for the "nuclear" option and assign a solid background color to the text I append. I thought I could do that with the following:
vSlices.append('text')
.attr('x', function(d,i) {return d.x+(j*300)})
.attr('y', function(d) {return d.y+5})
.attr('text-anchor','middle')
.style('fill', function(d) {return strokeMap[d.data.id]})
.style('background-color', function(d) {return colorMap[d.data.id]})
.text(function(d) {return d.data.data.size ? d.data.data.size : null});
However this achieved nothing.
Question
What is the correct way to assign background color for svg text dynamically? (seeing as the background color will need to match the circle's color as per colorMap)
A <rect> can be added before the <text> and given a background color.
const parent = vSlices.append("g").attr("class", "vSlices");
const rect = parent.append("rect")
.attr("x", "-10")
.attr("y", "-15")
.attr("height", 30)
.attr("rx", 15)
.attr("ry", 15)
.style("fill", "#f1f1f1");
const text = parent.append('text')
.attr('x', function(d,i) {return d.x+(j*300)})
.attr('y', function(d) {return d.y+5})
.style('fill', function(d) {return strokeMap[d.data.id]})
.text(function(d) {return d.data.data.size ? d.data.data.size : null});
rect.attr("width", function(d){
return this.parentNode.childNodes[1].getComputedTextLength() + 50
});
The canvas will be rendered as
<g class="vSlices" transform="translate(1020,230)">
<rect x="-10" y="-15" height="30" rx="15" ry="15" style="fill: rgb(241, 241, 241);" width="50.578125"></rect>
<text x="13" dy=".35em" text-anchor="start" style="fill-opacity: 1;">Data Size</text>
</g>
YOu can do it with css like this
p {
display: inline-block;
background-color: tomato;
}
It will give color only to the background of text. You can also use display: inline but then you won't be able to do changes to element like margins or smth else.
So use display: inline-block
I hope it helped :)
I am trying to turn this graph https://bl.ocks.org/scresawn/0e7e4cf9a0a459e59bacad492f73e139, into a react component. So far what I have is not rendering anything to the screen. If someone can take a look and make suggestions on improving the code that would be great. Also, I will be passing data to the component from another component instead fetching a CSV file. Any help will be greatly appreciated.
class ScaleTime extends Component {
constructor(){
super();
this.state = {
data: []
};
};
componentDidMount() {
fetch(data)
.then( (response) => {
return response.json() })
.then( (json) => {
this.setState({data: json});
});
};
//What is happening here?
componentWillMount(){
d3.csv("phage-history.csv", function (error, data) {
svgEnter = svg.selectAll("rect")
.data(data)
.enter();
svgEnter.append("rect")
.attr("rx", 25)
.attr("x", function (d) {
x = xScale(new Date(d.start));
return x;
})
.attr("y", function(d, i) { return 400 - (d.count*30); })
.attr("width", 25)
.attr("height", 25)
.attr("fill", "green")
.attr("stroke-width", "1px")
.attr("stroke", "black")
.on("mouseover", function (d) {
rect = d3.select(this);
rect.transition()
.duration(500)
.attr("y", function(d, i) {
console.log(this);
var x = rect.x;
return 20; })
.transition()
.duration(500)
.attr("rx", 2)
.attr("width", 300)
.attr("height", 100)
.attr("fill", "skyblue");
tooltip.html(d.authors + "<br>" + d.description);
tooltip
.style("top", 30)
.style("left",function () {
console.log("x", x);
return d3.event.pageX;
})
setTimeout(function () {
tooltip.style("visibility", "visible");
}, 1500);
})
.on("mouseout", function (d) {
d3.select(this)
.transition()
.duration(500)
.attr("rx", 25)
.attr("width", 25)
.attr("height", 25)
.transition()
.duration(500)
.delay(500)
.attr("y", function(d, i) { return 400 - (d.count*30); })
.attr("fill", "green");
//tooltip.text(d.authors);
return tooltip.style("visibility", "hidden");
});
});
}//componentWillMount
componentDidUpdate() {
var tooltip = d3.select("body")
.append("div")
.style("font-size", "12px")
.style("width", 285)
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden");
var svg = d3.select("body").append("svg")
.attr("width", 960)
.attr("height", 500);
var xScale = d3.scaleTime()
.domain([new Date("January 1, 1940 00:00:00"), new Date("January 4, 1980 00:00:00")])
.range([20, 900]);
var xAxis = d3.axisBottom(xScale);
}
render() {
return (
<div className="ScaleTime">
<svg width="960" height="500" />
svg.append("g")
.attr("transform", "translate(0,450)")
.call(xAxis);
</div>
);
}
}
I'm using click events to log data to the console, but i'd like to display this data in a separate box (which i have created). Does anyone have any advice or suggestions for this? Or is there a decent library that can help me achieve this?
Cheers
var circles = svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("r", 7)
.attr("cx", function(d) { return xScale(d[1]); })
.attr("cy", function(d) { return yScale(d[2]); })
.on('click', function(d, i) {
console.log("click", d[0]);
})
.attr("fill", function(d) {
var result = null;
if (data.indexOf(d) >= 0) {
result = colours(d);
} else {
result = "white";
}
return result;
});
var textBox = svg.append("rect")
.attr("x", 5)
.attr("y", 385)
.attr("height", 150)
.attr("width", 509)
.style("stroke", bordercolor)
.style("fill", "none")
.style("stroke-width", border);
In the "click" listener just select your box, or use the selection you already have:
circles.on("click", function(d) {
selection.append("text")
//etc...
})
Here is a simple demo, click the circle:
var svg = d3.select("svg");
var circle = svg.append("circle")
.datum({
name: "foo"
})
.attr("cx", 100)
.attr("cy", 100)
.attr("r", 60)
.style("fill", "teal");
var box = svg.append("g")
.attr("transform", "translate(300,50)");
box.append("rect")
.attr("height", 50)
.attr("width", 100)
.style("stroke", "black")
.style("fill", "none")
.style("stroke-width", "2px");
circle.on("click", function(d) {
box.append("text")
.attr("x", 10)
.attr("y", 20)
.text(d.name)
})
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg width="500" height="300"></svg>
Finally, two tips: make your selection a group or any other valid container for the text, not a rectangle, because you cannot append a text to a rectangle. Also, be prepared for all kinds of problems trying to fit your texts inside that rectangle: wrapping texts in an SVG is notoriously complicated.
I am trying to create a scrollable list. I have looked at other tutorials on here but it does not seem to be working. Essentially I am appending an SVG to a div. Inside this SVG is a D3JS stacked Bar Graph. to the right of this bar graph I am appending a 'g' element with an svg inside. I have set a height for this right SVG. Inside this I have populated a list that would extend beyond the height of the SVG. I have set the CSS for this svg to 'overflow-y: scroll'.
In spite of all of this I can not get this svg to scroll. Instead it just grows to the size of the list and extends past to intended bounds. Please See code below.
var barSVG = d3.select("#documents_reviewed_bar_chart").append("svg")
.classed('barChart-docs-reviewed', true)
.attr('id', 'barSVG')
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr('id', 'gElement')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var count = 0;
var graph = barSVG.append('g')
.attr('id', 'graphElement')
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "Date"; }));
data.forEach(function(d) {
var myDate = d.Date; //add to stock code
var y0 = 0;
d.people = color.domain().map(function(name) { return {myDate:myDate, name: name, y0: y0, y1: y0 += +d[name]}; });
d.total = d.people[d.people.length - 1].y1;
count = isNaN(d.total) ? count : count + d.total
});
x.domain(data.map(function(d) { return d.Date; }));
y.domain([0, d3.max(data, function(d) { return d.total; })]);
graph.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-65)" )
.style("cursor", "pointer")
.on('click', renderHorizontalChart);
graph.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end");
//.text("Population");
graph.append('text')
.text('Total: ' + count)
.attr('x', 20)
.attr('y', -10)
var state = graph.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + "0" + ",0)"; });
//.attr("transform", function(d) { return "translate(" + x(d.Date) + ",0)"; })
state.selectAll("rect")
.data(function(d) {
return d.people;
})
.enter().append("rect")
.attr("width", x.rangeBand())
.attr("y", height)
.attr("x",function(d) { //add to stock code
return x(d.myDate)
})
.attr("height", 0 )
.style("fill", function(d) { return color(d.name); })
.transition()
.duration(1000)
.attr("y", function(d) { return y(d.y1); })
.attr("height", function(d) { return y(d.y0) - y(d.y1); })
.attr("class", function(d) {
classLabel = d.name.replace(/,\s/g, ''); //remove spaces
return "class" + classLabel;
});
state.selectAll("rect")
.on("mouseover", function(d){
var delta = d.y1 - d.y0;
var xPos = parseFloat(d3.select(this).attr("x"));
var yPos = parseFloat(d3.select(this).attr("y"));
var height = parseFloat(d3.select(this).attr("height"))
d3.select(this).attr("stroke","black").attr("stroke-width",2);
tooltip.style("visibility", "visible");
tooltip.style("top", (event.pageY-10)+"px").style("left",(event.pageX+10)+"px");
tooltip.style('background', 'black')
tooltip.style('color', 'white')
tooltip.style('border-radius', '3px')
tooltip.style('padding', '5px')
tooltip.style('opacity', '0.8')
tooltip.style('font-size', '10px;')
tooltip.text(d.name +": "+ delta)
})
.on("mouseout",function(){
tooltip.style("visibility", "hidden");
graph.select(".tooltip").remove();
d3.select(this).attr("stroke","pink").attr("stroke-width",0.2);
})
var itemsAmount = 0
var rightSVG = barSVG.append('svg').classed('rightSVG', true)
.attr('height', '390')
.attr('id', 'rightSVG')
var legendSVG = rightSVG.append('svg').classed('legendSVG', true)
.attr('id', 'legendSVG')
var legend = legendSVG.selectAll(".legend")
.data(color.domain().slice().reverse())
.enter().append("g")
//.attr("class", "legend")
.attr("class", function (d) {
itemsAmount = itemsAmount + 1
legendClassArray.push(d.replace(/,\s/g, '')); //remove spaces
return "legend";
})
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
//reverse order to match order in which bars are stacked
legendClassArray = legendClassArray.reverse();
legend.append("rect")
.attr("x", width - 0)
.attr("width", 18)
.attr("height", 18)
.style("fill", color)
.attr("id", function (d, i) {
return "id#" + d.replace(/,\s/g, '');
})
.on("mouseover",function(){
if (active_link === "0") d3.select(this).style("cursor", "pointer");
else {
if (active_link.split("class").pop() === this.id.split("id#").pop()) {
d3.select(this).style("cursor", "pointer");
} else d3.select(this).style("cursor", "auto");
}
})
.on("click",function(d){
if (!this.id.includes('active')) { //nothing selected, turn on this selection
d3.select(this)
.attr('id', function(){
return this.id + 'active'
})
.style("stroke", "black")
.style("stroke-width", 2);
active_link = this.id.split("id#").pop();
plotSingle(this);
} else { //deactivate
d3.select(this)
.classed("active", false)
.attr('id', function() {
return this.id.replace('active', '')
})
.style("stroke", "none")
.style("stroke-width", 0);
plotSingle(this);
}
});
legend.append("text")
.attr("x", width - 6)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
legendSVG.append("text")
.classed('queryButton', true)
.attr("x", width - 6)
.attr("y", height)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text('Run Query')
.on('click', function(){
if (newArr.length > 0) {
d3.select('#barSVG').remove();
runScript(newArr)
}
});
legendSVG.append("text")
.classed('queryButton', true)
.attr("x", width - 6)
.attr("y", height + 18)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text('Reset')
The Specific SVG that I want to be scrollable is the 'rightSVG'.
As you can see in the image, the names are cut off. There should be a scrollable legend where I am able to see 29 data items.
Also, I have added the below CSS:
#documents_reviewed_bar_chart, #gElement{
max-height: 390;
overflow-y: scroll;
}
Short answer: you can't have a scrollable SVG inside another SVG. The overflow-y: scroll applies to HTML elements, not to SVG elements.
Alternative (and hacky) answer: technically, what you want is possible, but you'll have to wrap your inner SVG in an HTML element, which has to be inside a foreignObject.
This alternative is suboptimal, makes little sense and doesn't work on IE. However, just for the sake of curiosity, this is how you can do it:
var outerSvg = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 200)
.style("background-color", "darkkhaki");
var foreign = outerSvg.append("foreignObject")
.attr("x", 300)
.attr("y", 10)
.attr("width", 150)
.attr("height", 180)
.append("xhtml:div")
.style("max-height", "180px")
.style("overflow-y", "scroll");
var innerSvg = foreign.append("svg")
.attr("width", 133)
.attr("height", 1000)
.style("background-color", "powderblue");
var color = d3.scaleOrdinal(d3.schemeCategory20);
var texts = innerSvg.selectAll("foo")
.data(d3.range(65))
.enter()
.append("text")
.attr("x", 40)
.attr("y", (d,i)=> 20 + 15*i)
.text("foo bar baz")
var rects = innerSvg.selectAll("foo")
.data(d3.range(65))
.enter()
.append("rect")
.attr("x", 10)
.attr("y", (d,i)=> 8 + 15*i)
.attr("width", 20)
.attr("height", 13)
.attr("fill", (d,i)=>color(i));
<script src="https://d3js.org/d3.v4.min.js"></script>
The outer SVG is light brown (or khaki). The inner SVG, at the right, is blue, and it's inside a <div> with overflow-y: scroll;.
I'm trying to do a bar chart using d3.js version 4, I'm trying to make the vertical axis and it gives me the following error: Uncaught Error at Bn (d3.min.js:2) at Kn.vp [as ease] (d3.min.js:5) at main.js:88
here is my code:
var bardata = [];
for (var i=0; i < 20; i++){
bardata.push(Math.random())
}
bardata.sort(function compareNumbers(a,b){
return a -b;
})
var height = 400,
width = 600,
barWidth = 50,
barOffset = 5;
var tempColor;
var yScale = d3.scaleLinear()
.domain([0, d3.max(bardata)])
.range([0, height]);
var xScale = d3.scaleBand()
.domain(d3.range(0, bardata.length))
.padding(0.1)
.range([0, width]);
var tooltip = d3.select('body').append('div')
.style('position', 'absolute')
.style('padding', '0 10px')
.style('background', 'white')
.style('opacity', 0)
var myChart = d3.select('#chart').append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.style('background', '#C9D7D6')
.selectAll('rect').data(bardata)
.enter().append('rect')
.style('fill', '#C61C6F')
.attr('width', xScale.bandwidth())
.attr('x', function(d, i) {
return xScale(i);
})
.attr('height', 0)
.attr('y', height)
.on('mouseover', function(d){
d3.select(this)
.style('opacity', 0.5)
})
.on('mouseleave', function(d){
d3.select(this)
.style('opacity', 1)
})
.on('mouseover', function(d){
tooltip.transition()
.style('opacity', 0.9)
tooltip.html(d)
.style('left', (d3.event.pageX - 35) + 'px')
.style('top', (d3.event.pageY - 30) + 'px')
tempColor = this.style.fill;
d3.select(this)
.style('opacity', 0.5)
.style('fill', 'yellow')
})
.on('mouseleave', function(d){
tempColor = this.style.fill;
d3.select(this)
.style('opacity', 1)
.style('fill', '#C61C6F')
})
myChart.transition()
.attr('height', function(d){
return yScale(d);
})
.attr('y', function(d){
return height - yScale(d);
})
.delay(function(d, i){
return i * 20;
})
.duration(1000)
.ease('elastic')
var vAxis = d3.svg.axis()
.scale(yScale)
.orient('left')
.ticks(10)
var vGuide = d3.select('svg').append('g')
vAxis(vGuide)
vGuide.attr('transform', 'translate(35,0)')
<!DOCTYPE html>
<html>
<head>
<title>Line Chart</title>
<meta charset="8-UTF">
<link rel="stylesheet" src="css/style.css"> </head>
<body>
<div class="container">
<h2>Bar Chart</h2>
<div id="chart"></div>
</div>
<script src="js/d3.min.js"></script>
<script src="js/main.js"></script>
</body>
</html>
Instead of
myChart.transition()
.attr('height', function(d){
return yScale(d);
})
.attr('y', function(d){
return height - yScale(d);
})
.delay(function(d, i){
return i * 20;
})
.duration(1000)
.ease('elastic')//INCORRECT
It should have been
myChart.transition()
.attr('height', function(d){
return yScale(d);
})
.attr('y', function(d){
return height - yScale(d);
})
.delay(function(d, i){
return i * 20;
})
.duration(1000)
.ease(d3.easeElastic)
working code here