@@ -278,6 +278,16 @@ exports.loneHover = function loneHover(hoverItems, opts) {
278278 return multiHover ? hoverLabel : hoverLabel . node ( ) ;
279279} ;
280280
281+ // Helper function for use with spikeline logic
282+ // Returns true if either axis has spikeline settings which allow a spikeline to be drawn
283+ // to a point that is not currently hovered over; returns false otherwise.
284+ function canSpikeToNonHoveredPoint ( xa , ya ) {
285+ return Boolean (
286+ ( xa ?. showspikes && xa . spikesnap !== 'hovered data' ) ||
287+ ( ya ?. showspikes && ya . spikesnap !== 'hovered data' )
288+ ) ;
289+ }
290+
281291// The actual implementation is here:
282292function _hover ( gd , evt , subplot , noHoverEvent , eventTarget ) {
283293 if ( ! subplot ) subplot = 'xy' ;
@@ -708,53 +718,58 @@ function _hover(gd, evt, subplot, noHoverEvent, eventTarget) {
708718 }
709719 }
710720
711- // Now if there is range to look in, find the points to draw the spikelines
712- // Do it only if there is no hoverData
713- if ( hasCartesian && spikedistance !== 0 ) {
714- if ( hoverData . length === 0 ) {
715- pointData . distance = spikedistance ;
716- pointData . index = false ;
717- var closestPoints = trace . _module . hoverPoints ( pointData , xval , yval , 'closest' , {
718- hoverLayer : fullLayout . _hoverlayer
721+ // If there is no hoverData, and if the axis spikeline settings permit us to draw
722+ // a spikeline to the nearest non-hovered point, search for points to spike to.
723+ // This search is _expensive_ (especially when spikedistance is -1 which requires
724+ // a search over all points), so we should only do it when absolutely required
725+ if ( hasCartesian
726+ && hoverData . length === 0
727+ && spikedistance !== 0
728+ && canSpikeToNonHoveredPoint ( pointData . xa , pointData . ya )
729+ ) {
730+ // Find the points to draw the spikelines
731+ pointData . distance = spikedistance ;
732+ pointData . index = false ;
733+ var closestPoints = trace . _module . hoverPoints ( pointData , xval , yval , 'closest' , {
734+ hoverLayer : fullLayout . _hoverlayer
735+ } ) ;
736+ if ( closestPoints ) {
737+ closestPoints = closestPoints . filter ( function ( point ) {
738+ // some hover points, like scatter fills, do not allow spikes,
739+ // so will generate a hover point but without a valid spikeDistance
740+ return point . spikeDistance <= spikedistance ;
719741 } ) ;
720- if ( closestPoints ) {
721- closestPoints = closestPoints . filter ( function ( point ) {
722- // some hover points, like scatter fills, do not allow spikes,
723- // so will generate a hover point but without a valid spikeDistance
724- return point . spikeDistance <= spikedistance ;
725- } ) ;
726- }
727- if ( closestPoints && closestPoints . length ) {
728- var tmpPoint ;
729- var closestVPoints = closestPoints . filter ( function ( point ) {
730- return point . xa . showspikes && point . xa . spikesnap !== 'hovered data' ;
731- } ) ;
732- if ( closestVPoints . length ) {
733- var closestVPt = closestVPoints [ 0 ] ;
734- if ( isNumeric ( closestVPt . x0 ) && isNumeric ( closestVPt . y0 ) ) {
735- tmpPoint = fillSpikePoint ( closestVPt ) ;
736- if (
737- ! spikePoints . vLinePoint ||
738- spikePoints . vLinePoint . spikeDistance > tmpPoint . spikeDistance
739- ) {
740- spikePoints . vLinePoint = tmpPoint ;
741- }
742+ }
743+ if ( closestPoints ?. length ) {
744+ var tmpPoint ;
745+ var closestVPoints = closestPoints . filter ( function ( point ) {
746+ return point . xa . showspikes && point . xa . spikesnap !== 'hovered data' ;
747+ } ) ;
748+ if ( closestVPoints . length ) {
749+ var closestVPt = closestVPoints [ 0 ] ;
750+ if ( isNumeric ( closestVPt . x0 ) && isNumeric ( closestVPt . y0 ) ) {
751+ tmpPoint = fillSpikePoint ( closestVPt ) ;
752+ if (
753+ ! spikePoints . vLinePoint ||
754+ spikePoints . vLinePoint . spikeDistance > tmpPoint . spikeDistance
755+ ) {
756+ spikePoints . vLinePoint = tmpPoint ;
742757 }
743758 }
759+ }
744760
745- var closestHPoints = closestPoints . filter ( function ( point ) {
746- return point . ya . showspikes && point . ya . spikesnap !== 'hovered data' ;
747- } ) ;
748- if ( closestHPoints . length ) {
749- var closestHPt = closestHPoints [ 0 ] ;
750- if ( isNumeric ( closestHPt . x0 ) && isNumeric ( closestHPt . y0 ) ) {
751- tmpPoint = fillSpikePoint ( closestHPt ) ;
752- if (
753- ! spikePoints . hLinePoint ||
754- spikePoints . hLinePoint . spikeDistance > tmpPoint . spikeDistance
755- ) {
756- spikePoints . hLinePoint = tmpPoint ;
757- }
761+ var closestHPoints = closestPoints . filter ( function ( point ) {
762+ return point . ya . showspikes && point . ya . spikesnap !== 'hovered data' ;
763+ } ) ;
764+ if ( closestHPoints . length ) {
765+ var closestHPt = closestHPoints [ 0 ] ;
766+ if ( isNumeric ( closestHPt . x0 ) && isNumeric ( closestHPt . y0 ) ) {
767+ tmpPoint = fillSpikePoint ( closestHPt ) ;
768+ if (
769+ ! spikePoints . hLinePoint ||
770+ spikePoints . hLinePoint . spikeDistance > tmpPoint . spikeDistance
771+ ) {
772+ spikePoints . hLinePoint = tmpPoint ;
758773 }
759774 }
760775 }
@@ -1425,12 +1440,12 @@ function createHoverText(hoverData, opts) {
14251440 var mainText = ! unifiedhovertitleText
14261441 ? t0
14271442 : Lib . hovertemplateString ( {
1428- data :
1429- hovermode === 'x unified' ? [ { xa : item0 . xa , x : item0 . xVal } ] : [ { ya : item0 . ya , y : item0 . yVal } ] ,
1430- fallback : item0 . trace . hovertemplatefallback ,
1431- locale : fullLayout . _d3locale ,
1432- template : unifiedhovertitleText
1433- } ) ;
1443+ data :
1444+ hovermode === 'x unified' ? [ { xa : item0 . xa , x : item0 . xVal } ] : [ { ya : item0 . ya , y : item0 . yVal } ] ,
1445+ fallback : item0 . trace . hovertemplatefallback ,
1446+ locale : fullLayout . _d3locale ,
1447+ template : unifiedhovertitleText
1448+ } ) ;
14341449
14351450 var mockLayoutIn = {
14361451 showlegend : true ,
@@ -2329,12 +2344,12 @@ function cleanPoint(d, hovermode) {
23292344
23302345 var getVal = Array . isArray ( index )
23312346 ? function ( calcKey , traceKey ) {
2332- var v = Lib . castOption ( cd0 , index , calcKey ) ;
2333- return pass ( v ) ? v : Lib . extractOption ( { } , trace , '' , traceKey ) ;
2334- }
2347+ var v = Lib . castOption ( cd0 , index , calcKey ) ;
2348+ return pass ( v ) ? v : Lib . extractOption ( { } , trace , '' , traceKey ) ;
2349+ }
23352350 : function ( calcKey , traceKey ) {
2336- return Lib . extractOption ( cd , trace , calcKey , traceKey ) ;
2337- } ;
2351+ return Lib . extractOption ( cd , trace , calcKey , traceKey ) ;
2352+ } ;
23382353
23392354 function fill ( key , calcKey , traceKey ) {
23402355 var val = getVal ( calcKey , traceKey ) ;
0 commit comments