我正在使用d3来显示数据集中的数据。显示屏显示了层次结构的三个元素:
单击代表中间级别的DOM时,将显示最下面的级别。再次单击中间级别时,底部级别消失。一切都很好。
问题是我的用户总是被诱惑点击底层,而这样做却使底层消失了。我希望这样做,以便仅在单击中间层时才使底部层消失。
我尝试过的:
我尝试将事件侦听器放在中间级别的文本元素上,而不是div上。这导致错误d.key is not a function
。
我也尝试在on.click
之前加上div.parentNode
和divs2.parentNode
,但收到消息divs2.parentNode is undefined.
这是我的代码
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>
更新:我已经尝试在子节点上放置“停止传播”:
.on("mouseover", function(event) {
event.stopPropagation();
div.transition()
.duration(200)
.style("opacity", .9);
div.html(d3.select(this).datum().values[0].TooltipInfo)
但这似乎阻止了孩子的动作(出现工具提示),而不是阻止了父母的动作(孩子消失)。
UPDATE#2:stopPropagation
似乎不适用于mouseover
,而仅适用于click
。下面给出了我想要的行为(但是我仍然需要弄清楚如何使工具提示消失):
.on("click", function() {
event.stopPropagation();
div.transition()
.duration(200)
.style("opacity", .9);
div.html(d3.select(this).datum().values[0].TooltipInfo)
答案 0 :(得分:1)
当您单击最底层的SVG时,浏览器将尝试为所产生的click
事件找到合适的处理程序,该事件从鼠标(最一般而言,指针)位置的最底端元素开始。如果在该元素上未找到处理程序,则浏览器将向上遍历DOM树,检查其中是否包含任何内容。父级-已注册处理程序的元素,直到找到一个处理程序或到达根元素为止。此过程称为事件冒泡,如果您不熟悉它,则可能需要花一些时间来研究这个概念,因为它有助于理解JavaScript事件处理时的许多误解。可以找到涉及此主题的大量资源,例如:
要阻止click
事件冒泡到中级元素,从而导致它切换底层元素的可见性,您需要在底层元素本身上注册click
处理程序。在该处理程序中,您可以使用事件的stopPropagation
方法来防止事件再冒泡。
.on("click", () => d3.event.stopPropagation());
这样做,如果单击底层元素,则不会执行中层元素的处理程序,而如果单击中层元素本身,仍然可以访问它。
看看以下工作演示:
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);
})
.on("click", () => d3.event.stopPropagation());
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js"></script>
<div class="container"></div>
答案 1 :(得分:-1)
答案是三个不同元素的组合:
stopPropagation
。on.click
中添加一个条件
功能。请参见下面的完整示例。
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() {
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");
}
}, false);
divs2.append("text")
.attr('class', 'label1')
.attr('x', 0)
.attr('y', 0)
.style("font-size", "21px")
.text(function(d) {
return d.key;
})
var firstClick = 1;
var svgs2 = divs2.selectAll(null)
.data(function(d, e) {
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("click", function() {
event.stopPropagation()
if (firstClick % 2 === 1) {
div.transition()
.duration(200)
.style("opacity", .9)
div.html(d3.select(this).datum().values[0].TooltipInfo)
console.log(firstClick);
} else {
div.style("opacity", 0)
}
firstClick++;
})
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<div class="container"></div>