Reputation: 187
Firstly, I'm using d3.js and SVG. I've got this piece of code that draws a path:
var one = d3.select('#canvas1');
var oneCanvas = one.append("svg").attr("width", 200).attr("height", 190);
oneCanvas.append("svg:path")
.attr("d", "M100 15 A 55 55, 0, 0, 0, 73 61 A 55 55, 0, 0, 1, 127 61 A 64 64, 0, 0, 0, 100 15")
.style("stroke","black")
.style("fill", "white")
.style("stroke-width", 1)
.on("click", function(){
d3.select(this).style("fill", "magenta");
alert("You've clicked on the path in the 1st div");
});
I use the path about 20 times throughout. With this approach, I repeat the above code time and again as shown in https://jsfiddle.net/s26kghmq/
The reason I don't use 'd3.selectAll'
is that by doing so I won't be able to set different functions when the path is clicked.
I implemented svg's 'def'
and 'use'
, but that doesn't help me either because if I've set a style previously, I can't override it again in the 'use'
as discussed here: http://taye.me/blog/svg/a-guide-to-svg-use-elements/
I was wondering if there's an alternative approach to achieve the functionality without the repetition?
Thanks in advance for all your answers.
Upvotes: 1
Views: 82
Reputation: 2329
You could put the path drawing in a separate function as such
var drawPath = function(ele, clickFunction){
ele.append("svg:path")
.attr("d", "M100 15 A 55 55, 0, 0, 0, 73 61 A 55 55, 0, 0, 1, 127 61 A 64 64, 0, 0, 0, 100 15")
.style("stroke","black")
.style("fill", "white")
.style("stroke-width", 1)
.on("click", clickFunction)
}
and then reuse this function like this:
oneCanvas.call(drawPath, yourSpecificClickFunction)
Upvotes: 2
Reputation: 108512
It doesn't matter if the callback differs in the .on
event, there are many ways to handle that. d3
is all about data-binding, iteration and not duplicating code. So, you really, really should be using selectAll
.
var data = [1,2];
var color = d3.scale.ordinal()
.domain(data)
.range(['magenta','yellow']);
var one = d3.selectAll('.v1')
.data(data)
.append("svg")
.attr("width", 200)
.attr("height", 190)
.append("svg:path")
.attr("d", "M100 15 A 55 55, 0, 0, 0, 73 61 A 55 55, 0, 0, 1, 127 61 A 64 64, 0, 0, 0, 100 15")
.style("stroke","black")
.style("fill", "white")
.style("stroke-width", 1)
.on('click', function(d){
d3.select(this).style("fill", color(d));
alert("You've clicked on the path in the " + d + " div");
});
.v1{
width: 200px;
height: 100px;
background: white;
border: 1.5px solid #000000;
}
#canvas2{
position: absolute;
top: 150px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="canvas1" class="v1"></div>
<div id="canvas2" class="v1"></div>
Updates for comments
If the callbacks are unique per datum then they too just become attributes of the data. Further, you can always segment out shared and specific functionality:
function sharedFunc(elem,d){
d3.event.stopPropagation();
d3.select(elem).style('fill', d.color);
}
var data = [
{
specificFunc: function(d){
alert("you've clicked the 1st one");
},
color: 'magenta'
},{
specificFunc: function(d){
alert("you've clicked the 2nd one");
},
color: 'yellow'
}
];
d3.selectAll('.v1')
.data(data)
.append("svg")
.attr("width", 200)
.attr("height", 190)
.append("svg:path")
.attr("d", "M100 15 A 55 55, 0, 0, 0, 73 61 A 55 55, 0, 0, 1, 127 61 A 64 64, 0, 0, 0, 100 15")
.style("stroke","black")
.style("fill", "white")
.style("stroke-width", 1)
.on('click', function(d){
sharedFunc(this, d);
d.specificFunc(d);
});
$("#canvas1").click(function(){
alert("you clicked on the canvas");
});
.v1{
width: 200px;
height: 100px;
background: white;
border: 1.5px solid #000000;
}
.v2{
width: 200px;
height: 100px;
background: white;
border: 1.5px solid #000000;
}
#canvas2{
position: absolute;
top: 150px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="canvas1" class="v1"></div>
<div id="canvas2" class="v1"></div>
Upvotes: 2