Reputation: 7
How to left align the y-axis labels in d3? tried using the .orient(left) but it doesn't help. Tried transforming the labels. Please help on this aligning the labels to the left Example: Good Very Good Very Good1 Very Good1 Very1 Good Very Good Very Good1 Very Good1 Very1 Good Very Good Very Good1 Very Good1 Very1 Good Very Good Very Good1 Very Good1 Very1
const data = [{name: 'Good', value: 1}, {name: 'Very Good', value: 2}, {name: 'Very', value: 1}, {name: 'Good1', value: 1}, {name: 'Very Good1', value: 2}, {name: 'Very1', value: 1}, {name: 'Good2', value: 1}, {name: 'Very Good2', value: 2}, {name: 'Very2', value: 1},{name: 'Good3', value: 1}, {name: 'Very Good3', value: 2}, {name: 'Very3', value: 1},{name: 'Good4', value: 1}, {name: 'Very Good5', value: 2}, {name: 'Very5', value: 1},{name: 'Good6', value: 1}, {name: 'Very Good6', value: 2}, {name: 'Very6', value: 1},{name: 'Good7', value: 1}, {name: 'Very Good7', value: 2}, {name: 'Very7', value: 1},{name: 'Good8', value: 1}, {name: 'Very Good8', value: 2}, {name: 'Very9', value: 1}];
// We want to center each rect around the value it's supposed to have.
// That means that we need to have a node width
let nodeWidth = 33;
let nodeHeight = 18;
if (this.viewBy !== 'Overlapping ' + this.jobOrSkill) {
nodeWidth = 60;
nodeHeight = 18;
}
let height = 1000;
const width = 620,
paddingLeft = 0,
paddingTop = 0,
margin = {
top: 20,
left: 140,
right: 40,
bottom: 40
};
if (this.totalSkills && this.totalSkills.length < 10) {
height = 250;
}
if (this.totalSkills && this.totalSkills.length > 10 && this.totalSkills.length < 20) {
height = 400;
}
if (this.totalSkills && this.totalSkills.length > 20 && this.totalSkills.length < 30) {
height = 600;
}
const innerWidth = width + (margin.left + margin.right);
const innerHeight = height + (margin.top + margin.bottom);
// We also need to make sure there is space for all nodes, even at the edges.
// One way to get this is by just extending the domain a little.
const domain = d3.extent(data.map(d => Math.round(d.value)));
const x = d3.scaleLinear()
.domain([Math.round(domain[0]) - 1, Math.round(domain[1]) + 1])
.range([0, width]);
const y = d3.scaleBand()
.domain(data.map((d, i) => d.name && d.name.length > 20 ? d.name = d.name.slice(0, 20) + '...' : d.name = d.name))
.range([height, 0])
.padding(1);
const svg = d3.select('#comparisionChartData')
.attr('width', width + margin.left)
.attr('height', height + margin.top + margin.bottom);
const g = svg
.append('g')
.attr('transform', `translate(${margin.left} ${margin.right})`);
g.append('g')
.classed('x-axis', true)
.attr('transform', `translate(0, ${height})`)
.call(d3.axisBottom(x));
d3.selectAll('.y-axis').selectAll('.tick')
.append('svg:title')
.data(data)
.text(function (d) {
return d.name;
});
g.append('g')
.classed('y-axis', true)
.call(d3.axisLeft(y).tickPadding(10));
const bars = g.append('g')
.selectAll('rect')
.data(data)
.attr('stroke', '#e5e5e5')
.attr('class', 'line');
bars.exit().remove();
// All the same until here
bars.enter()
.append('rect')
// width has become a constant
.attr('width', nodeWidth)
// Now, transform each node so it centers around the value it's supposed to have
.attr('transform', `translate(${-nodeWidth / 2} -8)`)
// .merge(bars)
// `x` denotes the placement directly again
.attr('x', d => x(Math.round(d.value)))
.attr('y', d => y(d.name))
.attr('height', nodeHeight)
.attr('fill', d => Math.round(d.value) > 0 && this.viewBy === 'Overlapping ' + this.jobOrSkill ? '#648fff' :
this.viewBy === 'Overlapping ' + this.jobOrSkill && Math.round(d.value) === 0 ? '#9a16ca' :
this.viewBy === 'Overlapping ' + this.jobOrSkill && Math.round(d.value) < 0 ? '#dc267f' :
this.viewBy === 'Unique to Organization' ? '#016970' : this.viewBy === 'Absent to Organization' ? '#757575' : '#648fff');
// Now one more thing, we want to add labels to each node.
// `<rect>` can't have children, we we add them to the plot seperately
// using the same `data` as for the bars
const labels = g.append('g')
.selectAll('text')
.data(data);
labels.exit().remove();
labels.enter()
.append('text')
.attr('fill', 'white')
.attr('text-anchor', 'middle') // center-align the text
// .merge(bars)
// `x` denotes the placement directly
.attr('x', d => x(Math.round(d.value)))
// Add half a bar's height to target the center of each node
.attr('y', d => y(d.name) + y.bandwidth() / 4)
// Actually fill in the text this.viewBy === 'Overlapping ' + this.jobOrSkill ? '+' + Math.round(d.value) :
.text(d => Math.round(d.value) > 0 && this.viewBy === 'Overlapping ' + this.jobOrSkill ? Math.round(d.value)
: this.viewBy === 'Unique to Organization' ? 'Unique' : this.viewBy === 'Absent to Organization' ? 'Absent' : Math.round(d.value))
.attr('title', d => y(d.name))
.attr('transform', `translate(0, 0)`)
.attr('dy', 5);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg id="comparisionChartData"></svg>
Upvotes: 1
Views: 1372
Reputation: 13129
I was able to get it with a combination of text-anchor: start
and transform(-60, 0)
:
const data = [{
name: 'Good',
value: 1
}, {
name: 'Very Good',
value: 2
}, {
name: 'Very',
value: 1
}, {
name: 'Good1',
value: 1
}, {
name: 'Very Good1',
value: 2
}, {
name: 'Very1',
value: 1
}, {
name: 'Good2',
value: 1
}, {
name: 'Very Good2',
value: 2
}, {
name: 'Very2',
value: 1
}, {
name: 'Good3',
value: 1
}, {
name: 'Very Good3',
value: 2
}, {
name: 'Very3',
value: 1
}, {
name: 'Good4',
value: 1
}, {
name: 'Very Good5',
value: 2
}, {
name: 'Very5',
value: 1
}, {
name: 'Good6',
value: 1
}, {
name: 'Very Good6',
value: 2
}, {
name: 'Very6',
value: 1
}, {
name: 'Good7',
value: 1
}, {
name: 'Very Good7',
value: 2
}, {
name: 'Very7',
value: 1
}, {
name: 'Good8',
value: 1
}, {
name: 'Very Good8',
value: 2
}, {
name: 'Very9',
value: 1
}];
// We want to center each rect around the value it's supposed to have.
// That means that we need to have a node width
let nodeWidth = 33;
let nodeHeight = 18;
if (this.viewBy !== 'Overlapping ' + this.jobOrSkill) {
nodeWidth = 60;
nodeHeight = 18;
}
let height = 1000;
const width = 620,
paddingLeft = 0,
paddingTop = 0,
margin = {
top: 20,
left: 140,
right: 40,
bottom: 40
};
if (this.totalSkills && this.totalSkills.length < 10) {
height = 250;
}
if (this.totalSkills && this.totalSkills.length > 10 && this.totalSkills.length < 20) {
height = 400;
}
if (this.totalSkills && this.totalSkills.length > 20 && this.totalSkills.length < 30) {
height = 600;
}
const innerWidth = width + (margin.left + margin.right);
const innerHeight = height + (margin.top + margin.bottom);
// We also need to make sure there is space for all nodes, even at the edges.
// One way to get this is by just extending the domain a little.
const domain = d3.extent(data.map(d => Math.round(d.value)));
const x = d3.scaleLinear()
.domain([Math.round(domain[0]) - 1, Math.round(domain[1]) + 1])
.range([0, width]);
const y = d3.scaleBand()
.domain(data.map((d, i) => d.name && d.name.length > 20 ? d.name = d.name.slice(0, 20) + '...' : d.name = d.name))
.range([height, 0])
.padding(1);
const svg = d3.select('#comparisionChartData')
.attr('width', width + margin.left)
.attr('height', height + margin.top + margin.bottom);
const g = svg
.append('g')
.attr('transform', `translate(${margin.left} ${margin.right})`);
g.append('g')
.classed('x-axis', true)
.attr('transform', `translate(0, ${height})`)
.call(d3.axisBottom(x));
g.append('g')
.classed('y-axis', true)
.call(d3.axisLeft(y).tickPadding(10))
.selectAll("text")
.attr("transform", "translate(-60, 0)")
.attr("text-anchor", "start")
.append("title")
.text(function(d) {
return d;
});
const bars = g.append('g')
.selectAll('rect')
.data(data)
.attr('stroke', '#e5e5e5')
.attr('class', 'line');
bars.exit().remove();
// All the same until here
bars.enter()
.append('rect')
// width has become a constant
.attr('width', nodeWidth)
// Now, transform each node so it centers around the value it's supposed to have
.attr('transform', `translate(${-nodeWidth / 2} -8)`)
// .merge(bars)
// `x` denotes the placement directly again
.attr('x', d => x(Math.round(d.value)))
.attr('y', d => y(d.name))
.attr('height', nodeHeight)
.attr('fill', d => Math.round(d.value) > 0 && this.viewBy === 'Overlapping ' + this.jobOrSkill ? '#648fff' :
this.viewBy === 'Overlapping ' + this.jobOrSkill && Math.round(d.value) === 0 ? '#9a16ca' :
this.viewBy === 'Overlapping ' + this.jobOrSkill && Math.round(d.value) < 0 ? '#dc267f' :
this.viewBy === 'Unique to Organization' ? '#016970' : this.viewBy === 'Absent to Organization' ? '#757575' : '#648fff');
// Now one more thing, we want to add labels to each node.
// `<rect>` can't have children, we we add them to the plot seperately
// using the same `data` as for the bars
const labels = g.append('g')
.selectAll('text')
.data(data);
labels.exit().remove();
labels.enter()
.append('text')
.attr('fill', 'white')
.attr('text-anchor', 'middle') // center-align the text
// .merge(bars)
// `x` denotes the placement directly
.attr('x', d => x(Math.round(d.value)))
// Add half a bar's height to target the center of each node
.attr('y', d => y(d.name) + y.bandwidth() / 4)
// Actually fill in the text this.viewBy === 'Overlapping ' + this.jobOrSkill ? '+' + Math.round(d.value) :
.text(d => Math.round(d.value) > 0 && this.viewBy === 'Overlapping ' + this.jobOrSkill ? Math.round(d.value) :
this.viewBy === 'Unique to Organization' ? 'Unique' : this.viewBy === 'Absent to Organization' ? 'Absent' : Math.round(d.value))
.attr('title', d => y(d.name))
.attr('transform', `translate(0, 0)`)
.attr('dy', 5);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg id="comparisionChartData"></svg>
Upvotes: 2