D3 legend
Tags: codepen
Published: 2023-03-10 12:28:28

<p>A minimal demonstration of how to create an legend</p>

{% codepen https://codepen.io/shanu3742/pen/VwGyvrV %}

>##Step1:-complete the createLegend function 
- add **div** element with  class **chart** in your html file  where you want to add graph
```html
<div class="chart"></div>
<!--link d3 -->
<script src="https://d3js.org/d3.v6.min.js"  charset="utf-8"></script>
```
- 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
```javascript
   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
}
}
  }
```

- now we have to create a default color so if developer will not pass the color array we will consider the default color
```javascript
  //default color 
  // d3.schemeCategory10
  let colorScale =  d3.scaleOrdinal(data,  d3.schemeCategory10)

```
- now let's create the toolTip

```javascript


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))
}

```
- overall code for createLegend function
```javascript
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))
}
```

>##Step2:-initialize all require value 
```html
<div class="chart"></div>
<!--link d3 -->
<script src="https://d3js.org/d3.v6.min.js"  charset="utf-8"></script>
```
```javascript
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))
}
```
>##Step3:-call the function 
```javascript
createLegend(svgContainer,data1,width,height)
```

>##Step4:-change graph configuration according to your requirement
**legend config for the horizontal legend at top**

```javascript
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
}
```
> image 
![Screenshot (35)](https://user-images.githubusercontent.com/65018865/224311137-3fff811f-bb59-4a59-a058-7b52f8303b69.png)

**legend config for the vertical legend at center**
```javascript
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
}
}

```
>image
![Screenshot (36)](https://user-images.githubusercontent.com/65018865/224312222-10da09c8-a713-4648-9f4f-1c7c95a9f2cb.png)

**for color config you can assign array of color or just simply used d3 method to generate array of color**
```javascript
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
}
}


```
>image
![Screenshot (37)](https://user-images.githubusercontent.com/65018865/224313304-585b8dc9-199f-4fb2-9f80-128e0b6c29b7.png)

**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**
```javascript
  legendText:{
    length:15,//word size
    fullword:false
}
```
>image
![Screenshot (37)](https://user-images.githubusercontent.com/65018865/224313901-c7933cc4-e6eb-457d-8f8b-2ee342d917b6.png)

**if you want to dispaly whole word then set the property**
```javascript
 legendText:{
    length:15,
    fullword:true
}

```
>image
![Screenshot (38)](https://user-images.githubusercontent.com/65018865/224314353-64e8914b-c725-4cb8-89d2-9968c69e63ef.png)

#CODEBASE URL
[https://codepen.io/shanu3742/pen/VwGyvrV](https://codepen.io/shanu3742/pen/VwGyvrV)