nicki
nicki

Reputation: 187

Alternative way to write this?

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

Answers (2)

Matthijs Brouns
Matthijs Brouns

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

Mark
Mark

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

Related Questions