diff --git a/src/interactiveLayer.js b/src/interactiveLayer.js index c3cf982..eae281b 100644 --- a/src/interactiveLayer.js +++ b/src/interactiveLayer.js @@ -3,13 +3,15 @@ This places a rectangle on top of the chart. When you mouse move over it, it sen containing the X-coordinate. It can also render a vertical line where the mouse is located. */ -nv.interactiveLineLayer = function() { - +nv.interactiveGuideline = function() { + var tooltip = nv.models.tooltip(); //Public settings var width = null , height = null , xScale = d3.scale.linear() + , yScale = d3.scale.linear() , dispatch = d3.dispatch('elementMousemove', 'elementMouseout') + , showGuideLine = true ; //Private variables @@ -40,15 +42,16 @@ nv.interactiveLineLayer = function() { var mouseX = d3.mouse(this)[0]; var mouseY = d3.mouse(this)[1]; var pointIndex = Math.floor(xScale.invert(mouseX + padding)); - var pointLocation = xScale(pointIndex); + var pointXLocation = xScale(pointIndex); dispatch.elementMousemove({ mouseX: mouseX, mouseY: mouseY, pointIndex: pointIndex, - pointLocation: pointLocation + pointXLocation: pointXLocation, + pointYLocation: mouseY //TODO: Return the proper Y coordinate, not just mouseY. }); - showGuideLine(pointLocation); + renderGuideLine(pointXLocation); }) @@ -64,19 +67,20 @@ nv.interactiveLineLayer = function() { pointIndex: pointIndex }); - showGuideLine(null); + renderGuideLine(null); }) ; - function showGuideLine(x) { + function renderGuideLine(x) { + if (!showGuideLine) return; var line = wrap.select(".nv-interactiveGuideLine") .selectAll("line") .data((x != null) ? [x] : [], String); line.enter() .append("line") - .attr("stroke", "#ccc") + .attr("class", "nv-guideline") .attr("x1", function(d) { return d;}) .attr("x2", function(d) { return d;}) .attr("y1", availableHeight) @@ -89,6 +93,7 @@ nv.interactiveLineLayer = function() { } layer.dispatch = dispatch; + layer.tooltip = tooltip; layer.width = function(_) { if (!arguments.length) return width; @@ -108,6 +113,12 @@ nv.interactiveLineLayer = function() { return layer; }; + layer.showGuideLine = function(_) { + if (!arguments.length) return showGuideLine; + showGuideLine = _; + return layer; + }; + return layer; }; \ No newline at end of file diff --git a/src/models/lineChart.js b/src/models/lineChart.js index 49f6d0c..1ead295 100644 --- a/src/models/lineChart.js +++ b/src/models/lineChart.js @@ -9,8 +9,7 @@ nv.models.lineChart = function() { , xAxis = nv.models.axis() , yAxis = nv.models.axis() , legend = nv.models.legend() - , interactiveLayer = nv.interactiveLineLayer() - , tooltip = nv.models.tooltip() + , interactiveLayer = nv.interactiveGuideline() ; //set margin.right to 23 to fit dates on the x-axis within the chart @@ -22,10 +21,11 @@ nv.models.lineChart = function() { , showXAxis = true , showYAxis = true , rightAlignYAxis = false + , useInteractiveGuideline = false , tooltips = true - , tooltip = function(d) { - return '

' + d.key + '

' + - '

' + d.series[0].value + ' at ' + d.value + '

' + , tooltip = function(key, x, y, e, graph) { + return '

' + key + '

' + + '

' + y + ' at ' + x + '

' } , x , y @@ -51,29 +51,14 @@ nv.models.lineChart = function() { //------------------------------------------------------------ var showTooltip = function(e, offsetElement) { - var x = xAxis.tickFormat()(lines.x()(e.point, e.pointIndex)); - - var tip = nv.models.tooltip() - .position(e.position) - .chartContainer(offsetElement) - .gravity('w') - .distance(50) - .snapDistance(25) - .enabled(tooltips) - .valueFormatter(function(d,i) { - return yAxis.tickFormat()(d); - }) - .data( - { - key: "", - value: x, - // seriesSelectedKey: e.series.key, - series: e.allSeriesData - } - ) - .render() - ; - + if (useInteractiveGuideline) return; + var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ), + top = e.pos[1] + ( offsetElement.offsetTop || 0), + x = xAxis.tickFormat()(lines.x()(e.point, e.pointIndex)), + y = yAxis.tickFormat()(lines.y()(e.point, e.pointIndex)), + content = tooltip(e.series.key, x, y, e, chart); + + nv.tooltip.show([left, top], content, null, null, offsetElement); }; //============================================================ @@ -155,10 +140,10 @@ nv.models.lineChart = function() { //------------------------------------------------------------ //Set up interactive layer - interactiveLayer.width(availableWidth).height(availableHeight).xScale(x); - - wrap.select(".nv-interactive").call(interactiveLayer); - + if (useInteractiveGuideline) { + interactiveLayer.width(availableWidth).height(availableHeight).xScale(x); + wrap.select(".nv-interactive").call(interactiveLayer); + } //------------------------------------------------------------ // Legend @@ -194,7 +179,7 @@ nv.models.lineChart = function() { lines .width(availableWidth) .height(availableHeight) - .interactive(false) + .interactive(!useInteractiveGuideline) .color(data.map(function(d,i) { return d.color || color(d, i); }).filter(function(d,i) { return !data[i].disabled })); @@ -271,7 +256,7 @@ nv.models.lineChart = function() { interactiveLayer.dispatch.on('elementMousemove', function(e) { lines.dispatch.clearHighlights(); - var xValue, allData = []; + var singlePoint, allData = []; data .filter(function(series, i) { series.seriesIndex = i; @@ -281,19 +266,32 @@ nv.models.lineChart = function() { lines.dispatch.highlightPoint(i, e.pointIndex, true); var point = series.values[e.pointIndex]; if (typeof point === 'undefined') return; + if (typeof singlePoint === 'undefined') singlePoint = point; allData.push({ key: series.key, - value: lines.y()(series.values[e.pointIndex], e.pointIndex), + value: lines.y()(point, e.pointIndex), color: color(series,series.seriesIndex) }); }); - showTooltip({ - position: {left: e.pointLocation + margin.left, top: e.mouseY + margin.top}, - pointIndex: e.pointIndex, - allSeriesData: allData + var xValue = xAxis.tickFormat()(lines.x()(singlePoint,e.pointIndex)); + + interactiveLayer.tooltip + .position({left: e.pointXLocation + margin.left, top: e.mouseY + margin.top}) + .chartContainer(that.parentNode) + .enabled(tooltips) + .valueFormatter(function(d,i) { + return yAxis.tickFormat()(d); + }) + .data( + { + key: "", + value: xValue, + // seriesSelectedKey: e.series.key, + series: allData + } + )(); - }, that.parentNode); }); interactiveLayer.dispatch.on("elementMouseout",function(e) { @@ -333,11 +331,11 @@ nv.models.lineChart = function() { lines.dispatch.on('elementMouseover.tooltip', function(e) { e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top]; - // dispatch.tooltipShow(e); + dispatch.tooltipShow(e); }); lines.dispatch.on('elementMouseout.tooltip', function(e) { - // dispatch.tooltipHide(e); + dispatch.tooltipHide(e); }); dispatch.on('tooltipHide', function() { @@ -413,12 +411,23 @@ nv.models.lineChart = function() { return chart; }; + chart.useInteractiveGuideline = function(_) { + if(!arguments.length) return useInteractiveGuideline; + useInteractiveGuideline = _; + return chart; + }; + chart.tooltips = function(_) { if (!arguments.length) return tooltips; tooltips = _; return chart; }; + chart.tooltipContent = function(_) { + if (!arguments.length) return tooltip; + tooltip = _; + return chart; + }; chart.state = function(_) { if (!arguments.length) return state; diff --git a/src/models/scatter.js b/src/models/scatter.js index 09a724d..d3e9c45 100644 --- a/src/models/scatter.js +++ b/src/models/scatter.js @@ -248,22 +248,12 @@ nv.models.scatter = function() { var series = data[d.series], point = series.values[d.point]; - var allSeriesData = []; - data.forEach(function(item) { - allSeriesData.push({ - key: item.key, - value: getY(item.values[d.point], d.point), - color: color(item) - }); - }); - mDispatch({ point: point, series: series, pos: [x(getX(point, d.point)) + margin.left, y(getY(point, d.point)) + margin.top], seriesIndex: d.series, - pointIndex: d.point, - allSeriesData: allSeriesData + pointIndex: d.point }); }; @@ -446,8 +436,7 @@ nv.models.scatter = function() { dispatch.on('highlightPoint', function(seriesIndex, pointIndex, isHoverOver) { d3.select(".nv-chart-" + id + " .nv-series-" + seriesIndex + " .nv-point-" + pointIndex) - .classed("hover",isHoverOver); - + .classed("hover",isHoverOver); }); dispatch.on('elementMouseover.point', function(d) { if (interactive) dispatch.highlightPoint(d.seriesIndex,d.pointIndex,true); diff --git a/src/nv.d3.css b/src/nv.d3.css index 39ee117..6557967 100644 --- a/src/nv.d3.css +++ b/src/nv.d3.css @@ -725,3 +725,10 @@ svg .title { .nvd3 .axis text { text-shadow: 0 1px 0 #fff; } + +/**** +Interactive Layer +*/ +.nvd3 line.nv-guideline { + stroke: #ccc; +} \ No newline at end of file diff --git a/src/tooltip.js b/src/tooltip.js index 9bb512d..39a5043 100644 --- a/src/tooltip.js +++ b/src/tooltip.js @@ -11,8 +11,6 @@ /* Model which can be instantiated to handle tooltip rendering. */ window.nv.models.tooltip = function() { - var nvtooltip = {}; - var content = null //HTML contents of the tooltip. If null, the content is generated via the data variable. , data = null /* Tooltip data. If data is given in the proper format, a consistent tooltip is generated. Format of data: @@ -36,9 +34,9 @@ } */ - , gravity = 's' //Can be 'n','s','e','w'. Determines how tooltip is positioned. - , distance = 20 //Distance to offset tooltip from the mouse location. - , snapDistance = 30 + , gravity = 'w' //Can be 'n','s','e','w'. Determines how tooltip is positioned. + , distance = 50 //Distance to offset tooltip from the mouse location. + , snapDistance = 25 //Tolerance allowed before tooltip is moved from its current position (creates 'snapping' effect) , fixedTop = null //If not null, this fixes the top position of the tooltip. , classes = null //Attaches additional CSS classes to the tooltip DIV that is created. , chartContainer = null //Parent container that holds the chart. @@ -90,9 +88,27 @@ } } + //Creates new tooltip container, or uses existing one on DOM. + function getTooltipContainer(newContent) { + var container = document.getElementsByClassName("nvtooltip"); + if (container.length === 0) { + //Create new tooltip div if it doesn't exist on DOM. + container = document.createElement('div'); + container.className = 'nvtooltip ' + (classes ? classes : 'xy-tooltip'); + var body = document.getElementsByTagName('body')[0]; //All new tooltips are placed directly on + body.appendChild(container); + } + else { + //Element already exists on DOM, so reuse it. + container = container[0]; + } + + container.innerHTML = newContent; + return container; + } //Draw the tooltip onto the DOM. - nvtooltip.render = function() { + function nvtooltip() { if (!enabled) return; if (!dataSeriesExists(data)) return; @@ -107,16 +123,14 @@ } if (snapDistance && snapDistance > 0) { - // left = Math.floor(left/snapDistance) * snapDistance; top = Math.floor(top/snapDistance) * snapDistance; } - nv.tooltip.show([left, top], - contentGenerator(data), - gravity, - distance, - document.getElementsByTagName('body')[0], - classes); + var container = getTooltipContainer(contentGenerator(data)); + + + nv.tooltip.calcTooltipPosition([left,top], gravity, distance, container); + return nvtooltip; }; nvtooltip.content = function(_) { @@ -200,33 +214,27 @@ return nvtooltip; }; - nv.tooltip.show = function(pos, content, gravity, dist, parentContainer, classes) { - var container = document.getElementsByClassName("nvtooltip"); - if (container.length === 0) { - //Create new tooltip div if it doesn't exist on DOM. - container = document.createElement('div'); - container.className = 'nvtooltip ' + (classes ? classes : 'xy-tooltip'); - - var body = parentContainer; - if ( !parentContainer || parentContainer.tagName.match(/g|svg/i)) { - //If the parent element is an SVG element, place tooltip in the element. - body = document.getElementsByTagName('body')[0]; - } - - container.style.left = 0; - container.style.top = 0; - container.style.opacity = 0; - body.appendChild(container); - - } - else { - container = container[0]; + //Original tooltip.show function. Kept for backward compatibility. + nv.tooltip.show = function(pos, content, gravity, dist, parentContainer, classes) { + + //Create new tooltip div if it doesn't exist on DOM. + var container = document.createElement('div'); + container.className = 'nvtooltip ' + (classes ? classes : 'xy-tooltip'); + + var body = parentContainer; + if ( !parentContainer || parentContainer.tagName.match(/g|svg/i)) { + //If the parent element is an SVG element, place tooltip in the element. + body = document.getElementsByTagName('body')[0]; } + + container.style.left = 0; + container.style.top = 0; + container.style.opacity = 0; container.innerHTML = content; + body.appendChild(container); nv.tooltip.calcTooltipPosition(pos, gravity, dist, container); - }; //Global utility function to render a tooltip on the DOM. diff --git a/test/lineChartTest.html b/test/lineChartTest.html index c85b8f2..8a0e434 100644 --- a/test/lineChartTest.html +++ b/test/lineChartTest.html @@ -62,7 +62,7 @@ defaultChartConfig("chart1", sinAndCos(), true); //-------------- CHART 2 nv.addGraph(function() { var chart; - chart = nv.models.lineChart(); + chart = nv.models.lineChart().useInteractiveGuideline(false); chart .x(function(d,i) { return i }); @@ -97,7 +97,7 @@ defaultChartConfig("chart6", normalDist()); function defaultChartConfig(containerid, data, useDates) { nv.addGraph(function() { var chart; - chart = nv.models.lineChart(); + chart = nv.models.lineChart().useInteractiveGuideline(true); chart .x(function(d,i) { return i });