Search code examples
javascriptcsschartsdygraphs

Dygraphs: cannot set z-index for custom legend div


I am trying to implement a graph legend which will follow the mouse cursor. It works, but there is a problem: the legend div is drawn underneath the graph, which is not what I want.

See the following MWE:

<!DOCTYPE html>
<html>
  <head>
    <title>Testing</title>
    <script src="static/dygraph.min.js"></script>
    <link rel="stylesheet" href="static/dygraph.css" />
    <style>
      #graph {
        height: 400px;
        width: 640px;
      }
      #legend {
        position: absolute;
        background-color: red;
        /* z-index: 99; */
      }
    </style>
  </head>
  <body>
    <div id="legend"></div>
    <div id="graph"></div>
    <script>
      document.onmousemove = function(e) {
        var legend = document.getElementById("legend");
        legend.style.left = e.pageX + "px";
        legend.style.top = e.pageY + "px";
      };

      var data = [];
      for(let i = 0; i < 100; i++) {
        data.push([i, Math.random(), Math.random()]);
      }
      var g = new Dygraph(
        "graph",
        data,
        {
          fillGraph: true,
          fillAlpha: 0.8,
          labels: ["x", "y1", "y2"],
          labelsDiv: legend,
          legendFormatter: legendFormatter
        }
      );

      function legendFormatter(data) {
        if(data.x === null) return "";
        return data.xHTML + "<br />" +
               data.series.map(
                 v => "<span style='color:" + v.color + ";'>" +
                    v.labelHTML + "</span>: " + v.yHTML
               ).join("<br />");
      }
    </script>
  </body>
</html>

The following screenshot shows the current behaviour (which is not what I want):

Screenshot showing legend underneath graph

My natural instinct was to set the legend to have a higher z-index. However, doing so resulted in some weird behaviour.

In Firefox, the legend simply disappears.

In Chromium, the legend is not visible when the cursor is stationary (on the graph), and can be seen to flicker when the cursor is moving.

Why is this, and how do I make the legend appear correctly (on top of the graph)?

I still want the legend to be hidden when the cursor is off the graph, so setting #legend { display: block !important; } is not an option.


Solution

  • 1) when the legend div is on top, and is moved to the position of the cursor,
    this confuses the graph function that highlights the data point being hovered.
    you'll notice when the legend div disappears, no point on the graph is highlighted.
    this causes the flickering...

    to correct this issue, add a few pixels to the x,y position,
    so the legend div is not directly under the cursor.

    legend.style.left = (e.pageX + 16) + "px";
    legend.style.top = (e.pageY + 16) + "px";
    

    plus, the mouse hides part of the info otherwise...

    2) to place the legend div on top of the graph, without adding a z-index,
    add the legend div later in the dom, after the graph div...

    <div id="graph"></div>
    <div id="legend"></div>
    

    3) see following working snippet...

    <!DOCTYPE html>
    <html>
      <head>
        <title>Testing</title>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/dygraph/2.1.0/dygraph.min.js"></script>
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dygraph/2.1.0/dygraph.css" />
        <style>
          #graph {
            height: 400px;
            width: 640px;
          }
          #legend {
            position: absolute;
            background-color: red;
          }
        </style>
      </head>
      <body>
        <div id="graph"></div>
        <div id="legend"></div>
        <script>
          var legend = document.getElementById("legend");
          document.onmousemove = function(e) {
            legend.style.left = (e.pageX + 16) + "px";
            legend.style.top = (e.pageY + 16) + "px";
          };
    
          var data = [];
          for(let i = 0; i < 100; i++) {
            data.push([i, Math.random(), Math.random()]);
          }
          var g = new Dygraph(
            "graph",
            data,
            {
              fillGraph: true,
              fillAlpha: 0.8,
              labels: ["x", "y1", "y2"],
              labelsDiv: legend,
              legendFormatter: legendFormatter,
              
            }
          );
    
          function legendFormatter(data) {
            //if(data.x === null) return "";
            return data.xHTML + "<br />" +
                   data.series.map(
                     v => "<span style='color:" + v.color + ";'>" +
                        v.labelHTML + "</span>: " + v.yHTML
                   ).join("<br />");
          }
        </script>
      </body>
    </html>