DEV Community

KUMAR SHANU
KUMAR SHANU

Posted on

D3 legend

A minimal demonstration of how to create an legend

Step1:-complete the createLegend function

  • add div element with class chart in your html file where you want to add graph
<div class="chart"></div>
<!--link d3 -->
<script src="https://d3js.org/d3.v6.min.js"  charset="utf-8"></script>
Enter fullscreen mode Exit fullscreen mode
  • first create createLegend function and add require parameters selection-selection is basically svg element ,data- it's legend data what you want display,graphWidth and graphHeight it's height and width of your graph,legendConfig-general configuration for graph

  • first we will check whether config data is there or not inside our createLegend function

   if(!legendConfig){
    legendConfig={
        horizental:{
    isRequired:false,
    position:'top', //bottom or random positionPoint 20,30
},
  position:'top',
  vertical:{
    isRequired:false,
    positionAlongX:0,
    positionAlongY:50
  },
  legendColor:'',
  legendText:{
    length:15,
    fullword:true
}
}
  }
Enter fullscreen mode Exit fullscreen mode
  • now we have to create a default color so if developer will not pass the color array we will consider the default color
  //default color 
  // d3.schemeCategory10
  let colorScale =  d3.scaleOrdinal(data,  d3.schemeCategory10)

Enter fullscreen mode Exit fullscreen mode
  • now let's create the toolTip


let svgCon=selection.append('g')
//legendConfig.vertical.isRequired used to consider  legend should be vertical or horizental
.attr(`transform`,legendConfig.vertical.isRequired?
/**`translate(${legendConfig.vertical.positionAlongX},${legendConfig.vertical.positionAlongY})` where legend should be placed if it's vertiacll**/
`translate(${legendConfig.vertical.positionAlongX},${legendConfig.vertical.positionAlongY})`:`translate(20,50)`)
.selectAll('g')
.data(data)
.enter()
.append('g')
.attr('id',(d,i) => `tooltip${i}`)
.attr(`transform`,(d,i)=>rectPosition(legendConfig,i,graphHeight));
svgCon.append('text')
      .text((d,i) => 
//legendText function is used how text should be display
legendText(d,legendConfig?.legendText?.length,legendConfig?.legendText?.fullword))
      .attr('x', legendConfig.vertical.isRequired?20: (d,i) => {
if(i===0){
return 20;
}else{

  return calculateWidth(i);
}

})
svgCon.append('rect')
  .attr('width',20)
  .attr('height',20)
  .attr('fill', (d,i) =>{
  return !legendConfig.legendColor[i]? colorScale(d):legendConfig.legendColor[i]
})
  .attr('y',-15).attr('x',legendConfig.vertical.isRequired?-5:(d,i) => i===0?-5:calculateRectWidth(i))
}

Enter fullscreen mode Exit fullscreen mode
  • overall code for createLegend function
const rectPosition =(legendConfig,index,graphHeight) => {
  if(legendConfig.vertical.isRequired){
    return `translate(0,${index*25})`
}
  if(legendConfig.horizental.position.includes(',')){
    return `translate(${legendConfig.horizental.position})`
}
  return `translate(0,${legendConfig.horizental.position==='top'?0:legendConfig.horizental.position==='bottom'?graphHeight-60:0})`
}
const legendText=(text,count=15,fullword=false) => {
let result =''
if(fullword){
  result =text;
}else if(text.length>count && !fullword ){
    result = text.substring(0,count).concat('...')
}else{
  result =text;
}

  return result;
}
const createLegend= (selection,data,graphWidth,graphHeight) => {
  //default config
    if(!legendConfig){
    legendConfig={
        horizental:{
    isRequired:false,
    position:'top', //bottom or random positionPoint 20,30
},
  position:'top',
  vertical:{
    isRequired:false,
    positionAlongX:0,
    positionAlongY:50
  },
  legendColor:'',
  legendText:{
    length:15,
    fullword:true
}
}
  }
  //default color 
  // d3.schemeCategory10
  let colorScale =  d3.scaleOrdinal(data,  d3.schemeCategory10)
const calculateWidth = (length) => {

    let sum=0;
for(let i=length-1;i>=0;i--){
      let text_element =  d3.select(`#tooltip${i}`)
      sum= sum+ text_element.node().getBBox().width+60;

}
  return sum;
}
const calculateRectWidth = (length) => {

     return   d3.select(`#tooltip${length}`).select('text').attr('x')-25

}
let svgCon=selection.append('g')
.attr(`transform`,legendConfig.vertical.isRequired?`translate(${legendConfig.vertical.positionAlongX},${legendConfig.vertical.positionAlongY})`:`translate(20,50)`)
.selectAll('g')
.data(data)
.enter()
.append('g')
.attr('id',(d,i) => `tooltip${i}`)
.attr(`transform`,(d,i)=>rectPosition(legendConfig,i,graphHeight));
svgCon.append('text')
      .text((d,i) => legendText(d,legendConfig?.legendText?.length,legendConfig?.legendText?.fullword))
      .attr('x', legendConfig.vertical.isRequired?20: (d,i) => {
if(i===0){
return 20;
}else{

  return calculateWidth(i);
}

})
svgCon.append('rect')
  .attr('width',20)
  .attr('height',20)
  .attr('fill', (d,i) =>{
  return !legendConfig.legendColor[i]? colorScale(d):legendConfig.legendColor[i]
})
  .attr('y',-15).attr('x',legendConfig.vertical.isRequired?-5:(d,i) => i===0?-5:calculateRectWidth(i))
}
Enter fullscreen mode Exit fullscreen mode

Step2:-initialize all require value

<div class="chart"></div>
<!--link d3 -->
<script src="https://d3js.org/d3.v6.min.js"  charset="utf-8"></script>
Enter fullscreen mode Exit fullscreen mode
var data1 = ['Class A','Class B ','Class C','Class D','Class Matriculation','Class 12'];
let width=900;
let height =600;
let svgContainer =d3.select(".chart").append('svg').attr('width',width).attr('height',height)
let legendConfig={
  horizental:{
    position:'top', //bottom top  or random positionPoint 20,30
},
  position:'top',
  vertical:{
    isRequired:true,
    positionAlongX:450,
    positionAlongY:300
  },
  legendColor:['green','red','orange','yellow','pink'],
  legendText:{
    length:15,
    fullword:true
}
}
// legendConfig.horizental.position
const rectPosition =(legendConfig,index,graphHeight) => {
  if(legendConfig.vertical.isRequired){
    return `translate(0,${index*25})`
}
  if(legendConfig.horizental.position.includes(',')){
    return `translate(${legendConfig.horizental.position})`
}
  return `translate(0,${legendConfig.horizental.position==='top'?0:legendConfig.horizental.position==='bottom'?graphHeight-60:0})`
}
const legendText=(text,count=15,fullword=false) => {
let result =''
if(fullword){
  result =text;
}else if(text.length>count && !fullword ){
    result = text.substring(0,count).concat('...')
}else{
  result =text;
}

  return result;
}
const createLegend= (selection,data,graphWidth,graphHeight) => {
  //default config
    if(!legendConfig){
    legendConfig={
        horizental:{
    isRequired:false,
    position:'top', //bottom or random positionPoint 20,30
},
  position:'top',
  vertical:{
    isRequired:false,
    positionAlongX:0,
    positionAlongY:50
  },
  legendColor:'',
  legendText:{
    length:15,
    fullword:true
}
}
  }
  //default color 
  // d3.schemeCategory10
  let colorScale =  d3.scaleOrdinal(data,  d3.schemeCategory10)
const calculateWidth = (length) => {

    let sum=0;
for(let i=length-1;i>=0;i--){
      let text_element =  d3.select(`#tooltip${i}`)
      sum= sum+ text_element.node().getBBox().width+60;

}
  return sum;
}
const calculateRectWidth = (length) => {

     return   d3.select(`#tooltip${length}`).select('text').attr('x')-25

}
let svgCon=selection.append('g')
.attr(`transform`,legendConfig.vertical.isRequired?`translate(${legendConfig.vertical.positionAlongX},${legendConfig.vertical.positionAlongY})`:`translate(20,50)`)
.selectAll('g')
.data(data)
.enter()
.append('g')
.attr('id',(d,i) => `tooltip${i}`)
.attr(`transform`,(d,i)=>rectPosition(legendConfig,i,graphHeight));
svgCon.append('text')
      .text((d,i) => legendText(d,legendConfig?.legendText?.length,legendConfig?.legendText?.fullword))
      .attr('x', legendConfig.vertical.isRequired?20: (d,i) => {
if(i===0){
return 20;
}else{

  return calculateWidth(i);
}

})
svgCon.append('rect')
  .attr('width',20)
  .attr('height',20)
  .attr('fill', (d,i) =>{
  return !legendConfig.legendColor[i]? colorScale(d):legendConfig.legendColor[i]
})
  .attr('y',-15).attr('x',legendConfig.vertical.isRequired?-5:(d,i) => i===0?-5:calculateRectWidth(i))
}
Enter fullscreen mode Exit fullscreen mode

Step3:-call the function

createLegend(svgContainer,data1,width,height)
Enter fullscreen mode Exit fullscreen mode

Step4:-change graph configuration according to your requirement

legend config for the horizontal legend at top

let legendConfig={
  horizental:{
    position:'200,90', //bottom top  or random positionPoint 20,30
},
  position:'top',
  vertical:{
    isRequired:false,
    positionAlongX:450,
    positionAlongY:300
  },
  legendColor:['green','red','orange','yellow','pink'],
  legendText:{
    length:15,
    fullword:false
}
Enter fullscreen mode Exit fullscreen mode

image
Screenshot (35)

legend config for the vertical legend at center

let legendConfig={
  horizental:{
    position:'top', //bottom top  or random positionPoint 20,30
},
  position:'top',
  vertical:{
    isRequired:true,
    positionAlongX:450,//it should be width/2
    positionAlongY:300,//it should be height/2
  },
  legendColor:['green','red','orange','yellow','pink'],
  legendText:{
    length:15,
    fullword:false
}
}

Enter fullscreen mode Exit fullscreen mode

image
Screenshot (36)

for color config you can assign array of color or just simply used d3 method to generate array of color

let legendConfig={
  horizental:{
    position:'top', //bottom top  or random positionPoint 20,30
},
  position:'top',
  vertical:{
    isRequired:true,
    positionAlongX:450,
    positionAlongY:300
  },
  legendColor:d3.schemeCategory10,//used d3 method to get array of color
  legendText:{
    length:15,
    fullword:false
}
}


Enter fullscreen mode Exit fullscreen mode

image
Screenshot (37)

if you don't want to display whole word if text is large and you want to display upto some length then set the as given below

  legendText:{
    length:15,//word size
    fullword:false
}
Enter fullscreen mode Exit fullscreen mode

image
Screenshot (37)

if you want to dispaly whole word then set the property

 legendText:{
    length:15,
    fullword:true
}

Enter fullscreen mode Exit fullscreen mode

image
Screenshot (38)

CODEBASE URL

https://codepen.io/shanu3742/pen/VwGyvrV

Top comments (0)