I'm using d3 to display the data from a dataset. The display shows the three elements of a hierarchy:
- Top level: "Organization-sustaining activities"
- Middle level: "Extreme Sports"
- Bottom Level: "Skydiving management"
When the DOM representing the middle level is clicked, the bottom level appears. When the middle level is clicked a second time, the bottom level disappears. This is all good.
The problem is that my users are always tempted to click the bottom level, and doing so disappears the bottom-level. I'd like to make it so that the bottom-level only disappears when the middle-level is clicked.
What I've tried:
I tried putting the event listener on the middle-level text element rather than the div. This led to the error d.key is not a function
.
I also tried preceding on.click
with div.parentNode
and divs2.parentNode
but got the message divs2.parentNode is undefined.
Here is my code
var doc = URL.createObjectURL(new Blob([`TooltipInfo Category Function1 Function2
Records relating to the skydiving. Includes halters, parachutes, and altimeters.<ul><li>For records relating to rock climbing, see <b>rock climbing</b>.</li><li>For travel expenses, see <b>Procurements & Purchasing</b>.</li></ul>Retention:<ul><li>Keep records for seven years from the date of record creation, then send to <mark>archives.</mark></li><li>Keep all other records for seven years from the date of record creation, then destroy.</li></ul> • Skydiving Management Extreme Sports > Organization-sustaining Activities`]))
d3.tsv(doc)
.row(function(d) {
return {
University: d.University,
TooltipInfo: d.TooltipInfo,
Searchterms: d.Searchterms,
Category: d.Category,
Function1: d.Function1,
Function2: d.Function2,
MaxRetentionRounded: d.MaxRetentionRounded,
ModifiedRetention: d.ModifiedRetention
};
})
.get(function(error, data) {
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0)
var height = 150,
width = 300;
var nest = d3.nest()
.key(function(d) {
return d.Function2;
})
.key(function(d) {
return d.Function1;
})
.key(function(d) {
return d.Category;
})
.entries(data);
var height = 80,
width = 150;
var divs = d3.select(".container")
.selectAll(null)
.data(nest)
.enter()
.append("div")
.attr("class", "innerdiv");
divs.append("p")
.html(function(d) {
return d.key;
});
var divs2 = divs.selectAll(null)
.data(function(d) {
return d.values;
})
.enter()
.append('div')
.attr("class", "first")
.style("cursor", "pointer")
.on("click", function(d, i) {
const curColour = this.childNodes[1].attributes["height"].nodeValue;
if (curColour == '0px') {
d3.selectAll(this.childNodes).attr("height", "20px");
} else if (curColour == '0') {
d3.selectAll(this.childNodes).attr("height", "20px");
} else {
d3.selectAll(this.childNodes).attr("height", "0px");
}
});
divs2.append("text")
.attr('class', 'label1')
.attr('x', 0)
.attr('y', 0)
.style("font-size", "21px")
.text(function(d) {
return d.key;
})
var svgs2 = divs2.selectAll(null)
.data(function(d) {
return d.values;
})
.enter()
.append('svg')
.attr("class", "second")
.attr("height", 0)
.attr("width", function(d) {
return String(d3.select(this).value).length * 31.5
})
svgs2.append("text")
.attr('class', 'label2')
.attr('x', 10)
.attr('y', 17)
.style("font-size", "14px")
.text(function(d) {
return d.key;
})
.attr('text-anchor', 'start')
.style("cursor", "pointer")
.on("mouseover", function(d, i) {
div.transition()
.duration(200)
.style("opacity", .9);
div.html(d3.select(this).datum().values[0].TooltipInfo)
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js"></script>
<div class="container"></div>
UPDATE: I've tried putting a 'Stop Propagation' on the childnode:
.on("mouseover", function(event) {
event.stopPropagation();
div.transition()
.duration(200)
.style("opacity", .9);
div.html(d3.select(this).datum().values[0].TooltipInfo)
But it seems to stop the child's action (tooltip appearing) rather than the parents action (child disappearing).
UPDATE#2: stopPropagation
doesn't seem to apply to mouseover
, only to click
. The following gives the behaviour I want (but I still need to figure out how to disappear the tooltip):
.on("click", function() {
event.stopPropagation();
div.transition()
.duration(200)
.style("opacity", .9);
div.html(d3.select(this).datum().values[0].TooltipInfo)