Bootstrap 浮点图上的弹出窗口
Bootstrap popover on flot graph
我正在尝试在图表上添加弹出窗口。它不起作用。
var datasets = [{
"label": "Amend Existing Report",
"data": [{
"0": 1446422400000,
"1": 0
}, {
"0": 1447027200000,
"1": 0
}, {
"0": 1447632000000,
"1": 0
}, {
"0": 1448236800000,
"1": 0
}, {
"0": 1448841600000,
"1": 0
}, {
"0": 1449446400000,
"1": 1
}, {
"0": 1450051200000,
"1": 0
}, {
"0": 1450656000000,
"1": 0
}, {
"0": 1451260800000,
"1": 0
}, {
"0": 1451865600000,
"1": 0
}, {
"0": 1452470400000,
"1": 0
}, {
"0": 1453075200000,
"1": 1
}, {
"0": 1453680000000,
"1": 1
}, {
"0": 1454284800000,
"1": 0
}],
"idx": 0
}, {
"label": "Investigate Report Problem",
"data": [{
"0": 1446422400000,
"1": 1
}, {
"0": 1447027200000,
"1": 0
}, {
"0": 1447632000000,
"1": 2
}, {
"0": 1448236800000,
"1": 4
}, {
"0": 1448841600000,
"1": 0
}, {
"0": 1449446400000,
"1": 1
}, {
"0": 1450051200000,
"1": 0
}, {
"0": 1450656000000,
"1": 2
}, {
"0": 1451260800000,
"1": 0
}, {
"0": 1451865600000,
"1": 0
}, {
"0": 1452470400000,
"1": 0
}, {
"0": 1453075200000,
"1": 5
}, {
"0": 1453680000000,
"1": 0
}, {
"0": 1454284800000,
"1": 0
}],
"idx": 1
}, {
"label": "New Request (One Off Report)",
"data": [{
"0": 1446422400000,
"1": 0
}, {
"0": 1447027200000,
"1": 0
}, {
"0": 1447632000000,
"1": 1
}, {
"0": 1448236800000,
"1": 0
}, {
"0": 1448841600000,
"1": 0
}, {
"0": 1449446400000,
"1": 0
}, {
"0": 1450051200000,
"1": 0
}, {
"0": 1450656000000,
"1": 0
}, {
"0": 1451260800000,
"1": 0
}, {
"0": 1451865600000,
"1": 0
}, {
"0": 1452470400000,
"1": 0
}, {
"0": 1453075200000,
"1": 0
}, {
"0": 1453680000000,
"1": 1
}, {
"0": 1454284800000,
"1": 0
}],
"idx": 2
}, {
"label": "New Request (Regular Report)",
"data": [{
"0": 1446422400000,
"1": 4
}, {
"0": 1447027200000,
"1": 0
}, {
"0": 1447632000000,
"1": 2
}, {
"0": 1448236800000,
"1": 2
}, {
"0": 1448841600000,
"1": 0
}, {
"0": 1449446400000,
"1": 1
}, {
"0": 1450051200000,
"1": 0
}, {
"0": 1450656000000,
"1": 0
}, {
"0": 1451260800000,
"1": 1
}, {
"0": 1451865600000,
"1": 1
}, {
"0": 1452470400000,
"1": 0
}, {
"0": 1453075200000,
"1": 3
}, {
"0": 1453680000000,
"1": 2
}, {
"0": 1454284800000,
"1": 0
}],
"idx": 3
}, {
"label": "Other",
"data": [{
"0": 1446422400000,
"1": 0
}, {
"0": 1447027200000,
"1": 0
}, {
"0": 1447632000000,
"1": 2
}, {
"0": 1448236800000,
"1": 4
}, {
"0": 1448841600000,
"1": 2
}, {
"0": 1449446400000,
"1": 0
}, {
"0": 1450051200000,
"1": 2
}, {
"0": 1450656000000,
"1": 0
}, {
"0": 1451260800000,
"1": 0
}, {
"0": 1451865600000,
"1": 0
}, {
"0": 1452470400000,
"1": 3
}, {
"0": 1453075200000,
"1": 0
}, {
"0": 1453680000000,
"1": 3
}, {
"0": 1454284800000,
"1": 0
}],
"idx": 4
}, {
"label": "Special Events",
"data": [{
"0": 1446422400000,
"1": 0
}, {
"0": 1447027200000,
"1": 0
}, {
"0": 1447632000000,
"1": 0
}, {
"0": 1448236800000,
"1": 1
}, {
"0": 1448841600000,
"1": 0
}, {
"0": 1449446400000,
"1": 3
}, {
"0": 1450051200000,
"1": 1
}, {
"0": 1450656000000,
"1": 0
}, {
"0": 1451260800000,
"1": 0
}, {
"0": 1451865600000,
"1": 0
}, {
"0": 1452470400000,
"1": 0
}, {
"0": 1453075200000,
"1": 0
}, {
"0": 1453680000000,
"1": 0
}, {
"0": 1454284800000,
"1": 0
}],
"idx": 5
}];
var ticks = [];
for (var i = 0; i < datasets[0].data.length; i++) {
ticks.push(datasets[0].data[i][0]);
}
var options = {
"legend": {
"position": "ne",
"noColumns": 6
},
"yaxis": {
"min": 0
},
"xaxis": {
"mode": "time",
"timeformat": "%d %b",
// "tickSize": [7, "day"],
ticks: ticks,
"min": 1446163200000,
"max": 1454544000000 // 1454284800000
},
"grid": {
"clickable": true,
"hoverable": true
},
"series": {
"stack": true,
"bars": {
"show": true,
"barWidth": 181440000.00000003,
align: 'center'
}
}
};
$.plot($('#CAGraph'), datasets, options);
$("#CAGraph").bind("plothover",function(event, pos, item) {
if (item) {
//console.log(item);
var epoch = new Date(item.datapoint[0]);
var percent = item.datapoint[1].toFixed(0);
$('#tooltip').attr("data-original-title", item.series.label);
$('#tooltip').attr("data-content", (percent) + "<br>Total: " + item.datapoint[1]);
$("#tooltip").popover("show");
$("#tooltip").popover({
html: true,
title : function() {
return $(".popover-title").html();
},
content : function() {
return $(".popover-content").html();
}
});
$(".popover").css({
top : item.pageY,
left : item.pageX + 10
});
$(".popover.right>.arrow").css({
top : "20%",
});
} else {
$('#tooltip').attr("title","");
$('#tooltip').attr("data-content", "");
$("#tooltip").popover("hide");
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://rawgit.com/flot/flot/master/jquery.flot.js"></script>
<script src="https://rawgit.com/Codicode/flotanimator/master/jquery.flot.animator.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/flot/0.8.3/jquery.flot.time.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/flot/0.8.3/jquery.flot.stack.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/flot.tooltip/0.8.5/jquery.flot.tooltip.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.11.2/moment.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" integrity="sha256-7s5uDGW3AHqw6xtJmNNtr+OBRJUlgkNJEo78P4b0yRw= sha512-nNo+yCHEyn0smMxSswnf/OnX6/KwJuZTlNZBjauKhTK0c+zT+q5JOCx0UFhXQ6rJR9jg6Es8gPuD2uZcYDLqSw==" crossorigin="anonymous">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha256-KXn5puMvxCw+dAYznun+drMdG1IFl3agK0p/pqT9KAo= sha512-2e8qq0ETcfWRI4HJBzQiA3UoyFk6tbNyG+qSaIBZLyW9Xf3sWZHN/lxe9fTh1U45DpPf07yj94KsUHHWe4Yk1A==" crossorigin="anonymous"></script>
<div id="choices_CAGraph"></div>
<div id="CAGraph" style="width:910px;height:400px"></div>
<div id=tooltip class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>
工具提示 div 的 ID 在 HTML 中是错误的:tootltip
而不是 tooltip
。
并且在 JavaScript 中图表容器的 ID 是错误的:$("CAGraph").bind("plothover", ...
而不是 $("#CAGraph").bind("plothover",...
修复这两个错误后,弹出窗口显示但没有内容。
更新: 我让弹出窗口显示一些内容,但无法让它始终如一地工作。这可能是因为 popover show method is asnyc:
.popover('show')
Reveals an element's popover. Returns to the caller before the popover has actually been shown (i.e. before the shown.bs.popover
event occurs). This is considered a "manual" triggering of the popover. Popovers whose both title and content are zero-length are never displayed.
回退到手动生成工具提示,但效果很好。查看更新后的代码片段:
var datasets = [{
"label": "Amend Existing Report",
"data": [{
"0": 1446422400000,
"1": 0
}, {
"0": 1447027200000,
"1": 0
}, {
"0": 1447632000000,
"1": 0
}, {
"0": 1448236800000,
"1": 0
}, {
"0": 1448841600000,
"1": 0
}, {
"0": 1449446400000,
"1": 1
}, {
"0": 1450051200000,
"1": 0
}, {
"0": 1450656000000,
"1": 0
}, {
"0": 1451260800000,
"1": 0
}, {
"0": 1451865600000,
"1": 0
}, {
"0": 1452470400000,
"1": 0
}, {
"0": 1453075200000,
"1": 1
}, {
"0": 1453680000000,
"1": 1
}, {
"0": 1454284800000,
"1": 0
}],
"idx": 0
}, {
"label": "Investigate Report Problem",
"data": [{
"0": 1446422400000,
"1": 1
}, {
"0": 1447027200000,
"1": 0
}, {
"0": 1447632000000,
"1": 2
}, {
"0": 1448236800000,
"1": 4
}, {
"0": 1448841600000,
"1": 0
}, {
"0": 1449446400000,
"1": 1
}, {
"0": 1450051200000,
"1": 0
}, {
"0": 1450656000000,
"1": 2
}, {
"0": 1451260800000,
"1": 0
}, {
"0": 1451865600000,
"1": 0
}, {
"0": 1452470400000,
"1": 0
}, {
"0": 1453075200000,
"1": 5
}, {
"0": 1453680000000,
"1": 0
}, {
"0": 1454284800000,
"1": 0
}],
"idx": 1
}, {
"label": "New Request (One Off Report)",
"data": [{
"0": 1446422400000,
"1": 0
}, {
"0": 1447027200000,
"1": 0
}, {
"0": 1447632000000,
"1": 1
}, {
"0": 1448236800000,
"1": 0
}, {
"0": 1448841600000,
"1": 0
}, {
"0": 1449446400000,
"1": 0
}, {
"0": 1450051200000,
"1": 0
}, {
"0": 1450656000000,
"1": 0
}, {
"0": 1451260800000,
"1": 0
}, {
"0": 1451865600000,
"1": 0
}, {
"0": 1452470400000,
"1": 0
}, {
"0": 1453075200000,
"1": 0
}, {
"0": 1453680000000,
"1": 1
}, {
"0": 1454284800000,
"1": 0
}],
"idx": 2
}, {
"label": "New Request (Regular Report)",
"data": [{
"0": 1446422400000,
"1": 4
}, {
"0": 1447027200000,
"1": 0
}, {
"0": 1447632000000,
"1": 2
}, {
"0": 1448236800000,
"1": 2
}, {
"0": 1448841600000,
"1": 0
}, {
"0": 1449446400000,
"1": 1
}, {
"0": 1450051200000,
"1": 0
}, {
"0": 1450656000000,
"1": 0
}, {
"0": 1451260800000,
"1": 1
}, {
"0": 1451865600000,
"1": 1
}, {
"0": 1452470400000,
"1": 0
}, {
"0": 1453075200000,
"1": 3
}, {
"0": 1453680000000,
"1": 2
}, {
"0": 1454284800000,
"1": 0
}],
"idx": 3
}, {
"label": "Other",
"data": [{
"0": 1446422400000,
"1": 0
}, {
"0": 1447027200000,
"1": 0
}, {
"0": 1447632000000,
"1": 2
}, {
"0": 1448236800000,
"1": 4
}, {
"0": 1448841600000,
"1": 2
}, {
"0": 1449446400000,
"1": 0
}, {
"0": 1450051200000,
"1": 2
}, {
"0": 1450656000000,
"1": 0
}, {
"0": 1451260800000,
"1": 0
}, {
"0": 1451865600000,
"1": 0
}, {
"0": 1452470400000,
"1": 3
}, {
"0": 1453075200000,
"1": 0
}, {
"0": 1453680000000,
"1": 3
}, {
"0": 1454284800000,
"1": 0
}],
"idx": 4
}, {
"label": "Special Events",
"data": [{
"0": 1446422400000,
"1": 0
}, {
"0": 1447027200000,
"1": 0
}, {
"0": 1447632000000,
"1": 0
}, {
"0": 1448236800000,
"1": 1
}, {
"0": 1448841600000,
"1": 0
}, {
"0": 1449446400000,
"1": 3
}, {
"0": 1450051200000,
"1": 1
}, {
"0": 1450656000000,
"1": 0
}, {
"0": 1451260800000,
"1": 0
}, {
"0": 1451865600000,
"1": 0
}, {
"0": 1452470400000,
"1": 0
}, {
"0": 1453075200000,
"1": 0
}, {
"0": 1453680000000,
"1": 0
}, {
"0": 1454284800000,
"1": 0
}],
"idx": 5
}];
var ticks = [];
for (var i = 0; i < datasets[0].data.length; i++) {
ticks.push(datasets[0].data[i][0]);
}
var options = {
"legend": {
"position": "ne",
"noColumns": 6
},
"yaxis": {
"min": 0
},
"xaxis": {
"mode": "time",
"timeformat": "%d %b",
// "tickSize": [7, "day"],
ticks: ticks,
"min": 1446163200000,
"max": 1454544000000 // 1454284800000
},
"grid": {
"clickable": true,
"hoverable": true
},
"series": {
"stack": true,
"bars": {
"show": true,
"barWidth": 181440000.00000003,
align: 'center'
}
}
};
$.plot($('#CAGraph'), datasets, options);
$("#CAGraph").bind("plothover", function(event, pos, item) {
if (item) {
var epoch = new Date(item.datapoint[0]);
var percent = item.datapoint[1] - item.datapoint[2];
$("#tooltip").html(item.series.label + " " + (percent) + "<br>Total: " + item.datapoint[1]).css({
top: item.pageY - 25,
left: item.pageX + 10,
padding: 5
}).fadeIn(200);
} else {
$("#tooltip").hide();
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://rawgit.com/flot/flot/master/jquery.flot.js"></script>
<script src="https://rawgit.com/Codicode/flotanimator/master/jquery.flot.animator.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/flot/0.8.3/jquery.flot.time.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/flot/0.8.3/jquery.flot.stack.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/flot.tooltip/0.8.5/jquery.flot.tooltip.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.11.2/moment.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" integrity="sha256-7s5uDGW3AHqw6xtJmNNtr+OBRJUlgkNJEo78P4b0yRw= sha512-nNo+yCHEyn0smMxSswnf/OnX6/KwJuZTlNZBjauKhTK0c+zT+q5JOCx0UFhXQ6rJR9jg6Es8gPuD2uZcYDLqSw=="
crossorigin="anonymous">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha256-KXn5puMvxCw+dAYznun+drMdG1IFl3agK0p/pqT9KAo= sha512-2e8qq0ETcfWRI4HJBzQiA3UoyFk6tbNyG+qSaIBZLyW9Xf3sWZHN/lxe9fTh1U45DpPf07yj94KsUHHWe4Yk1A==" crossorigin="anonymous"></script>
<div id="choices_CAGraph"></div>
<div id="CAGraph" style="width:910px;height:400px"></div>
<div id=tooltip class="popover" role="tooltip">
<div class="arrow"></div>
<h3 class="popover-title"></h3>
<div class="popover-content"></div>
</div>
好的,任何解决方案都必须考虑 Bootstrap 的弹出窗口功能的异步行为,因为 Raidri 之前在几条评论中正确说明了这一点。此外,它还必须考虑到 plothover
事件将比异步弹出窗口 show/hide 调用完成要快得多的事实。换句话说,您将不得不特别注意系统的状态。
这让我明白,作为悬停事件处理程序的一部分,一遍又一遍地创建弹出窗口 object 是一个 no-no。它必须创建一次,然后才显示和隐藏。
我还注意到,在您的最新代码中,您再次忽略了我之前的观点,即标题和内容属性是字符串 或 函数 return 字符串。您 returning jQuery object 是您的 - 错误。
首先,我创建了一个新的 jQuery 函数。这将帮助我维护闭包中所需的状态,包括弹出窗口 object.
$.fn.popoverTooltip = function (selector, popoverSelector) {
// the rest of the code forming a nice closure
}
$.plot('#CAGraph', datasets, options);
$("#tooltip").popoverTooltip("#CAGraph", ".popover");
作为封闭代码的一部分,我在顶部创建了一些局部变量:
var barIdShown = null;
var chart = $(selector);
var tooltip = $(this);
var popoverProcessor = function () {
// mysterious code maintaining state
}();
然后是一个名为 popoverProcessor
的新 object(稍后将显示的代码),它将完成大部分实际工作并保持状态。
在该代码之后,我创建了实际的弹出窗口并绑定了一些事件处理程序。首先:我需要知道 何时 弹出 hide/show 功能 实际上 完成,所以我向相关的 BS 弹出事件添加了处理程序。第二:我将处理程序绑定到 plothover
事件以处理显示或隐藏工具提示。
//create popover
tooltip.popover({
html: true,
title : popoverProcessor.getTitle,
content : popoverProcessor.getContent
});
// bind events to know when shown or hidden
tooltip.on("hidden.bs.popover", popoverProcessor.hideDone);
tooltip.on("shown.bs.popover", popoverProcessor.showDone);
// bind hover event to chart
chart.bind("plothover", function(event, pos, item) {
var thisBarId;
if (item) {
thisBarId = seriesIndex * 10000 + dataIndex;
if (thisBarId !== barIdShown) {
if (barIdShown) {
popoverProcessor.hide();
}
popoverProcessor.setItem(item);
popoverProcessor.show();
barIdShown = thisBarId;
}
}
else {
if (barIdShown) {
popoverProcessor.hide();
barIdShown = null;
}
}
});
首先请注意,我在 popoverProcessor
到 return 工具提示的标题和内容中使用了函数。然后为了知道光标是否悬停在另一个条形段上而不移出条形,我创建了一个特殊的 "bar identifier"。 (如果它发生变化,我会在 re-showing 之前隐藏弹出窗口。)注意,在这个处理程序中 "synchronous" 一切都很好;异步部分在这个神秘的 popoverProcessor
object.
中处理
var popoverProcessor = function () {
var item = null;
var state = "hidden";
var taskQueue = [];
var showPopover = function () {
tooltip.popover("show");
$(popoverSelector).css({
top : item.pageY,
left : item.pageX + 10
});
$(".popover.right > .arrow").css({
top : "20%",
});
state = "showing";
};
var hidePopover = function () {
tooltip.popover("hide");
state = "hiding";
};
var processNextTask = function () {
var task;
if (taskQueue.length > 0) {
task = taskQueue.shift();
if (task === "show") {
showPopover();
}
else {
hidePopover();
}
}
};
return {
setItem: function (newItem) { item = newItem; },
getTitle: function () {
if (item) {
return item.series.label;
}
return "unknown item";
},
getContent: function () {
var percent;
if (item) {
percent = item.datapoint[1].toFixed(0);
return percent.toString() + "<br />Total: " + item.datapoint[1];
}
return "unknown item";
},
hideDone: function () {
state = "hidden";
processNextTask();
},
showDone: function () {
state = "shown";
processNextTask();
},
hide: function () {
if (state === "shown") {
hidePopover();
}
else {
taskQueue.push("hide");
}
},
show: function () {
if (state === "hidden") {
showPopover();
}
else {
taskQueue.push("show");
}
}
};
}();
publicobject有一套方法。您可以设置正在处理的项目,您可以获取弹出窗口的标题和内容,您可以发出弹出窗口已显示(或已隐藏)的信号,您可以请求显示或隐藏弹出窗口。
处理器维护弹出框的当前状态object。它们是:"hidden"、"showing"、"shown" 和 "hiding"。如果你调用hide()
并且状态是"shown",代码立即调用内部函数hidePopover
开始隐藏popover,否则一个项目被添加到任务queue到指示应尽可能隐藏弹出窗口。如果你调用 show()
.
也会发生类似的事情
有趣的事情发生在事件处理程序 showDone()
和 hideDone()
中。这是从任务 queue 弹出并处理下一个任务的地方。使用此任务 queue,我在 Bootstrap 异步环境中维护 hide/show 调用的顺序,确保仅在前一个完成时才启动新的显示状态更改。
另请注意,当调用 .popover("show")
时,工具提示的标题和内容实际上是通过提供的函数计算的。
毫无疑问,可以重构此代码以使其更简单,但我已经完成了。
我正在尝试在图表上添加弹出窗口。它不起作用。
var datasets = [{
"label": "Amend Existing Report",
"data": [{
"0": 1446422400000,
"1": 0
}, {
"0": 1447027200000,
"1": 0
}, {
"0": 1447632000000,
"1": 0
}, {
"0": 1448236800000,
"1": 0
}, {
"0": 1448841600000,
"1": 0
}, {
"0": 1449446400000,
"1": 1
}, {
"0": 1450051200000,
"1": 0
}, {
"0": 1450656000000,
"1": 0
}, {
"0": 1451260800000,
"1": 0
}, {
"0": 1451865600000,
"1": 0
}, {
"0": 1452470400000,
"1": 0
}, {
"0": 1453075200000,
"1": 1
}, {
"0": 1453680000000,
"1": 1
}, {
"0": 1454284800000,
"1": 0
}],
"idx": 0
}, {
"label": "Investigate Report Problem",
"data": [{
"0": 1446422400000,
"1": 1
}, {
"0": 1447027200000,
"1": 0
}, {
"0": 1447632000000,
"1": 2
}, {
"0": 1448236800000,
"1": 4
}, {
"0": 1448841600000,
"1": 0
}, {
"0": 1449446400000,
"1": 1
}, {
"0": 1450051200000,
"1": 0
}, {
"0": 1450656000000,
"1": 2
}, {
"0": 1451260800000,
"1": 0
}, {
"0": 1451865600000,
"1": 0
}, {
"0": 1452470400000,
"1": 0
}, {
"0": 1453075200000,
"1": 5
}, {
"0": 1453680000000,
"1": 0
}, {
"0": 1454284800000,
"1": 0
}],
"idx": 1
}, {
"label": "New Request (One Off Report)",
"data": [{
"0": 1446422400000,
"1": 0
}, {
"0": 1447027200000,
"1": 0
}, {
"0": 1447632000000,
"1": 1
}, {
"0": 1448236800000,
"1": 0
}, {
"0": 1448841600000,
"1": 0
}, {
"0": 1449446400000,
"1": 0
}, {
"0": 1450051200000,
"1": 0
}, {
"0": 1450656000000,
"1": 0
}, {
"0": 1451260800000,
"1": 0
}, {
"0": 1451865600000,
"1": 0
}, {
"0": 1452470400000,
"1": 0
}, {
"0": 1453075200000,
"1": 0
}, {
"0": 1453680000000,
"1": 1
}, {
"0": 1454284800000,
"1": 0
}],
"idx": 2
}, {
"label": "New Request (Regular Report)",
"data": [{
"0": 1446422400000,
"1": 4
}, {
"0": 1447027200000,
"1": 0
}, {
"0": 1447632000000,
"1": 2
}, {
"0": 1448236800000,
"1": 2
}, {
"0": 1448841600000,
"1": 0
}, {
"0": 1449446400000,
"1": 1
}, {
"0": 1450051200000,
"1": 0
}, {
"0": 1450656000000,
"1": 0
}, {
"0": 1451260800000,
"1": 1
}, {
"0": 1451865600000,
"1": 1
}, {
"0": 1452470400000,
"1": 0
}, {
"0": 1453075200000,
"1": 3
}, {
"0": 1453680000000,
"1": 2
}, {
"0": 1454284800000,
"1": 0
}],
"idx": 3
}, {
"label": "Other",
"data": [{
"0": 1446422400000,
"1": 0
}, {
"0": 1447027200000,
"1": 0
}, {
"0": 1447632000000,
"1": 2
}, {
"0": 1448236800000,
"1": 4
}, {
"0": 1448841600000,
"1": 2
}, {
"0": 1449446400000,
"1": 0
}, {
"0": 1450051200000,
"1": 2
}, {
"0": 1450656000000,
"1": 0
}, {
"0": 1451260800000,
"1": 0
}, {
"0": 1451865600000,
"1": 0
}, {
"0": 1452470400000,
"1": 3
}, {
"0": 1453075200000,
"1": 0
}, {
"0": 1453680000000,
"1": 3
}, {
"0": 1454284800000,
"1": 0
}],
"idx": 4
}, {
"label": "Special Events",
"data": [{
"0": 1446422400000,
"1": 0
}, {
"0": 1447027200000,
"1": 0
}, {
"0": 1447632000000,
"1": 0
}, {
"0": 1448236800000,
"1": 1
}, {
"0": 1448841600000,
"1": 0
}, {
"0": 1449446400000,
"1": 3
}, {
"0": 1450051200000,
"1": 1
}, {
"0": 1450656000000,
"1": 0
}, {
"0": 1451260800000,
"1": 0
}, {
"0": 1451865600000,
"1": 0
}, {
"0": 1452470400000,
"1": 0
}, {
"0": 1453075200000,
"1": 0
}, {
"0": 1453680000000,
"1": 0
}, {
"0": 1454284800000,
"1": 0
}],
"idx": 5
}];
var ticks = [];
for (var i = 0; i < datasets[0].data.length; i++) {
ticks.push(datasets[0].data[i][0]);
}
var options = {
"legend": {
"position": "ne",
"noColumns": 6
},
"yaxis": {
"min": 0
},
"xaxis": {
"mode": "time",
"timeformat": "%d %b",
// "tickSize": [7, "day"],
ticks: ticks,
"min": 1446163200000,
"max": 1454544000000 // 1454284800000
},
"grid": {
"clickable": true,
"hoverable": true
},
"series": {
"stack": true,
"bars": {
"show": true,
"barWidth": 181440000.00000003,
align: 'center'
}
}
};
$.plot($('#CAGraph'), datasets, options);
$("#CAGraph").bind("plothover",function(event, pos, item) {
if (item) {
//console.log(item);
var epoch = new Date(item.datapoint[0]);
var percent = item.datapoint[1].toFixed(0);
$('#tooltip').attr("data-original-title", item.series.label);
$('#tooltip').attr("data-content", (percent) + "<br>Total: " + item.datapoint[1]);
$("#tooltip").popover("show");
$("#tooltip").popover({
html: true,
title : function() {
return $(".popover-title").html();
},
content : function() {
return $(".popover-content").html();
}
});
$(".popover").css({
top : item.pageY,
left : item.pageX + 10
});
$(".popover.right>.arrow").css({
top : "20%",
});
} else {
$('#tooltip').attr("title","");
$('#tooltip').attr("data-content", "");
$("#tooltip").popover("hide");
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://rawgit.com/flot/flot/master/jquery.flot.js"></script>
<script src="https://rawgit.com/Codicode/flotanimator/master/jquery.flot.animator.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/flot/0.8.3/jquery.flot.time.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/flot/0.8.3/jquery.flot.stack.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/flot.tooltip/0.8.5/jquery.flot.tooltip.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.11.2/moment.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" integrity="sha256-7s5uDGW3AHqw6xtJmNNtr+OBRJUlgkNJEo78P4b0yRw= sha512-nNo+yCHEyn0smMxSswnf/OnX6/KwJuZTlNZBjauKhTK0c+zT+q5JOCx0UFhXQ6rJR9jg6Es8gPuD2uZcYDLqSw==" crossorigin="anonymous">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha256-KXn5puMvxCw+dAYznun+drMdG1IFl3agK0p/pqT9KAo= sha512-2e8qq0ETcfWRI4HJBzQiA3UoyFk6tbNyG+qSaIBZLyW9Xf3sWZHN/lxe9fTh1U45DpPf07yj94KsUHHWe4Yk1A==" crossorigin="anonymous"></script>
<div id="choices_CAGraph"></div>
<div id="CAGraph" style="width:910px;height:400px"></div>
<div id=tooltip class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>
工具提示 div 的 ID 在 HTML 中是错误的:tootltip
而不是 tooltip
。
并且在 JavaScript 中图表容器的 ID 是错误的:$("CAGraph").bind("plothover", ...
而不是 $("#CAGraph").bind("plothover",...
修复这两个错误后,弹出窗口显示但没有内容。
更新: 我让弹出窗口显示一些内容,但无法让它始终如一地工作。这可能是因为 popover show method is asnyc:
.popover('show')
Reveals an element's popover. Returns to the caller before the popover has actually been shown (i.e. before the
shown.bs.popover
event occurs). This is considered a "manual" triggering of the popover. Popovers whose both title and content are zero-length are never displayed.
回退到手动生成工具提示,但效果很好。查看更新后的代码片段:
var datasets = [{
"label": "Amend Existing Report",
"data": [{
"0": 1446422400000,
"1": 0
}, {
"0": 1447027200000,
"1": 0
}, {
"0": 1447632000000,
"1": 0
}, {
"0": 1448236800000,
"1": 0
}, {
"0": 1448841600000,
"1": 0
}, {
"0": 1449446400000,
"1": 1
}, {
"0": 1450051200000,
"1": 0
}, {
"0": 1450656000000,
"1": 0
}, {
"0": 1451260800000,
"1": 0
}, {
"0": 1451865600000,
"1": 0
}, {
"0": 1452470400000,
"1": 0
}, {
"0": 1453075200000,
"1": 1
}, {
"0": 1453680000000,
"1": 1
}, {
"0": 1454284800000,
"1": 0
}],
"idx": 0
}, {
"label": "Investigate Report Problem",
"data": [{
"0": 1446422400000,
"1": 1
}, {
"0": 1447027200000,
"1": 0
}, {
"0": 1447632000000,
"1": 2
}, {
"0": 1448236800000,
"1": 4
}, {
"0": 1448841600000,
"1": 0
}, {
"0": 1449446400000,
"1": 1
}, {
"0": 1450051200000,
"1": 0
}, {
"0": 1450656000000,
"1": 2
}, {
"0": 1451260800000,
"1": 0
}, {
"0": 1451865600000,
"1": 0
}, {
"0": 1452470400000,
"1": 0
}, {
"0": 1453075200000,
"1": 5
}, {
"0": 1453680000000,
"1": 0
}, {
"0": 1454284800000,
"1": 0
}],
"idx": 1
}, {
"label": "New Request (One Off Report)",
"data": [{
"0": 1446422400000,
"1": 0
}, {
"0": 1447027200000,
"1": 0
}, {
"0": 1447632000000,
"1": 1
}, {
"0": 1448236800000,
"1": 0
}, {
"0": 1448841600000,
"1": 0
}, {
"0": 1449446400000,
"1": 0
}, {
"0": 1450051200000,
"1": 0
}, {
"0": 1450656000000,
"1": 0
}, {
"0": 1451260800000,
"1": 0
}, {
"0": 1451865600000,
"1": 0
}, {
"0": 1452470400000,
"1": 0
}, {
"0": 1453075200000,
"1": 0
}, {
"0": 1453680000000,
"1": 1
}, {
"0": 1454284800000,
"1": 0
}],
"idx": 2
}, {
"label": "New Request (Regular Report)",
"data": [{
"0": 1446422400000,
"1": 4
}, {
"0": 1447027200000,
"1": 0
}, {
"0": 1447632000000,
"1": 2
}, {
"0": 1448236800000,
"1": 2
}, {
"0": 1448841600000,
"1": 0
}, {
"0": 1449446400000,
"1": 1
}, {
"0": 1450051200000,
"1": 0
}, {
"0": 1450656000000,
"1": 0
}, {
"0": 1451260800000,
"1": 1
}, {
"0": 1451865600000,
"1": 1
}, {
"0": 1452470400000,
"1": 0
}, {
"0": 1453075200000,
"1": 3
}, {
"0": 1453680000000,
"1": 2
}, {
"0": 1454284800000,
"1": 0
}],
"idx": 3
}, {
"label": "Other",
"data": [{
"0": 1446422400000,
"1": 0
}, {
"0": 1447027200000,
"1": 0
}, {
"0": 1447632000000,
"1": 2
}, {
"0": 1448236800000,
"1": 4
}, {
"0": 1448841600000,
"1": 2
}, {
"0": 1449446400000,
"1": 0
}, {
"0": 1450051200000,
"1": 2
}, {
"0": 1450656000000,
"1": 0
}, {
"0": 1451260800000,
"1": 0
}, {
"0": 1451865600000,
"1": 0
}, {
"0": 1452470400000,
"1": 3
}, {
"0": 1453075200000,
"1": 0
}, {
"0": 1453680000000,
"1": 3
}, {
"0": 1454284800000,
"1": 0
}],
"idx": 4
}, {
"label": "Special Events",
"data": [{
"0": 1446422400000,
"1": 0
}, {
"0": 1447027200000,
"1": 0
}, {
"0": 1447632000000,
"1": 0
}, {
"0": 1448236800000,
"1": 1
}, {
"0": 1448841600000,
"1": 0
}, {
"0": 1449446400000,
"1": 3
}, {
"0": 1450051200000,
"1": 1
}, {
"0": 1450656000000,
"1": 0
}, {
"0": 1451260800000,
"1": 0
}, {
"0": 1451865600000,
"1": 0
}, {
"0": 1452470400000,
"1": 0
}, {
"0": 1453075200000,
"1": 0
}, {
"0": 1453680000000,
"1": 0
}, {
"0": 1454284800000,
"1": 0
}],
"idx": 5
}];
var ticks = [];
for (var i = 0; i < datasets[0].data.length; i++) {
ticks.push(datasets[0].data[i][0]);
}
var options = {
"legend": {
"position": "ne",
"noColumns": 6
},
"yaxis": {
"min": 0
},
"xaxis": {
"mode": "time",
"timeformat": "%d %b",
// "tickSize": [7, "day"],
ticks: ticks,
"min": 1446163200000,
"max": 1454544000000 // 1454284800000
},
"grid": {
"clickable": true,
"hoverable": true
},
"series": {
"stack": true,
"bars": {
"show": true,
"barWidth": 181440000.00000003,
align: 'center'
}
}
};
$.plot($('#CAGraph'), datasets, options);
$("#CAGraph").bind("plothover", function(event, pos, item) {
if (item) {
var epoch = new Date(item.datapoint[0]);
var percent = item.datapoint[1] - item.datapoint[2];
$("#tooltip").html(item.series.label + " " + (percent) + "<br>Total: " + item.datapoint[1]).css({
top: item.pageY - 25,
left: item.pageX + 10,
padding: 5
}).fadeIn(200);
} else {
$("#tooltip").hide();
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://rawgit.com/flot/flot/master/jquery.flot.js"></script>
<script src="https://rawgit.com/Codicode/flotanimator/master/jquery.flot.animator.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/flot/0.8.3/jquery.flot.time.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/flot/0.8.3/jquery.flot.stack.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/flot.tooltip/0.8.5/jquery.flot.tooltip.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.11.2/moment.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" integrity="sha256-7s5uDGW3AHqw6xtJmNNtr+OBRJUlgkNJEo78P4b0yRw= sha512-nNo+yCHEyn0smMxSswnf/OnX6/KwJuZTlNZBjauKhTK0c+zT+q5JOCx0UFhXQ6rJR9jg6Es8gPuD2uZcYDLqSw=="
crossorigin="anonymous">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha256-KXn5puMvxCw+dAYznun+drMdG1IFl3agK0p/pqT9KAo= sha512-2e8qq0ETcfWRI4HJBzQiA3UoyFk6tbNyG+qSaIBZLyW9Xf3sWZHN/lxe9fTh1U45DpPf07yj94KsUHHWe4Yk1A==" crossorigin="anonymous"></script>
<div id="choices_CAGraph"></div>
<div id="CAGraph" style="width:910px;height:400px"></div>
<div id=tooltip class="popover" role="tooltip">
<div class="arrow"></div>
<h3 class="popover-title"></h3>
<div class="popover-content"></div>
</div>
好的,任何解决方案都必须考虑 Bootstrap 的弹出窗口功能的异步行为,因为 Raidri 之前在几条评论中正确说明了这一点。此外,它还必须考虑到 plothover
事件将比异步弹出窗口 show/hide 调用完成要快得多的事实。换句话说,您将不得不特别注意系统的状态。
这让我明白,作为悬停事件处理程序的一部分,一遍又一遍地创建弹出窗口 object 是一个 no-no。它必须创建一次,然后才显示和隐藏。
我还注意到,在您的最新代码中,您再次忽略了我之前的观点,即标题和内容属性是字符串 或 函数 return 字符串。您 returning jQuery object 是您的 - 错误。
首先,我创建了一个新的 jQuery 函数。这将帮助我维护闭包中所需的状态,包括弹出窗口 object.
$.fn.popoverTooltip = function (selector, popoverSelector) {
// the rest of the code forming a nice closure
}
$.plot('#CAGraph', datasets, options);
$("#tooltip").popoverTooltip("#CAGraph", ".popover");
作为封闭代码的一部分,我在顶部创建了一些局部变量:
var barIdShown = null;
var chart = $(selector);
var tooltip = $(this);
var popoverProcessor = function () {
// mysterious code maintaining state
}();
然后是一个名为 popoverProcessor
的新 object(稍后将显示的代码),它将完成大部分实际工作并保持状态。
在该代码之后,我创建了实际的弹出窗口并绑定了一些事件处理程序。首先:我需要知道 何时 弹出 hide/show 功能 实际上 完成,所以我向相关的 BS 弹出事件添加了处理程序。第二:我将处理程序绑定到 plothover
事件以处理显示或隐藏工具提示。
//create popover
tooltip.popover({
html: true,
title : popoverProcessor.getTitle,
content : popoverProcessor.getContent
});
// bind events to know when shown or hidden
tooltip.on("hidden.bs.popover", popoverProcessor.hideDone);
tooltip.on("shown.bs.popover", popoverProcessor.showDone);
// bind hover event to chart
chart.bind("plothover", function(event, pos, item) {
var thisBarId;
if (item) {
thisBarId = seriesIndex * 10000 + dataIndex;
if (thisBarId !== barIdShown) {
if (barIdShown) {
popoverProcessor.hide();
}
popoverProcessor.setItem(item);
popoverProcessor.show();
barIdShown = thisBarId;
}
}
else {
if (barIdShown) {
popoverProcessor.hide();
barIdShown = null;
}
}
});
首先请注意,我在 popoverProcessor
到 return 工具提示的标题和内容中使用了函数。然后为了知道光标是否悬停在另一个条形段上而不移出条形,我创建了一个特殊的 "bar identifier"。 (如果它发生变化,我会在 re-showing 之前隐藏弹出窗口。)注意,在这个处理程序中 "synchronous" 一切都很好;异步部分在这个神秘的 popoverProcessor
object.
var popoverProcessor = function () {
var item = null;
var state = "hidden";
var taskQueue = [];
var showPopover = function () {
tooltip.popover("show");
$(popoverSelector).css({
top : item.pageY,
left : item.pageX + 10
});
$(".popover.right > .arrow").css({
top : "20%",
});
state = "showing";
};
var hidePopover = function () {
tooltip.popover("hide");
state = "hiding";
};
var processNextTask = function () {
var task;
if (taskQueue.length > 0) {
task = taskQueue.shift();
if (task === "show") {
showPopover();
}
else {
hidePopover();
}
}
};
return {
setItem: function (newItem) { item = newItem; },
getTitle: function () {
if (item) {
return item.series.label;
}
return "unknown item";
},
getContent: function () {
var percent;
if (item) {
percent = item.datapoint[1].toFixed(0);
return percent.toString() + "<br />Total: " + item.datapoint[1];
}
return "unknown item";
},
hideDone: function () {
state = "hidden";
processNextTask();
},
showDone: function () {
state = "shown";
processNextTask();
},
hide: function () {
if (state === "shown") {
hidePopover();
}
else {
taskQueue.push("hide");
}
},
show: function () {
if (state === "hidden") {
showPopover();
}
else {
taskQueue.push("show");
}
}
};
}();
publicobject有一套方法。您可以设置正在处理的项目,您可以获取弹出窗口的标题和内容,您可以发出弹出窗口已显示(或已隐藏)的信号,您可以请求显示或隐藏弹出窗口。
处理器维护弹出框的当前状态object。它们是:"hidden"、"showing"、"shown" 和 "hiding"。如果你调用hide()
并且状态是"shown",代码立即调用内部函数hidePopover
开始隐藏popover,否则一个项目被添加到任务queue到指示应尽可能隐藏弹出窗口。如果你调用 show()
.
有趣的事情发生在事件处理程序 showDone()
和 hideDone()
中。这是从任务 queue 弹出并处理下一个任务的地方。使用此任务 queue,我在 Bootstrap 异步环境中维护 hide/show 调用的顺序,确保仅在前一个完成时才启动新的显示状态更改。
另请注意,当调用 .popover("show")
时,工具提示的标题和内容实际上是通过提供的函数计算的。
毫无疑问,可以重构此代码以使其更简单,但我已经完成了。