Search code examples
hovervega-litevega

Update text mark on hovering different marks in Vega


In my Vega specification I have 3 marks: a bar, a text related to that bar, and a text ("Hovered EventType details mark") that is supposed to plot the corresponding details when a particular bar / related text is hovered.

I never managed to make this work.

This is the current Vega specification:

{
  "$schema": "https://vega.github.io/schema/vega/v5.json",
  "description": "The PM2.5 value of Beijing observed 15 keys, highlighting the keys when PM2.5 level is hazardous to human health. Data source https://chartaccent.github.io/chartaccent.html",
  "background": "white",
  "padding": 5,
  "height": 250,
  "width": 500,
  "autosize": {
    "type": "fit",
    "resize": true
  },
  "data": [
    {
      "name": "source",
      "values": [
        { "key": "TYPE 1", "doc_count": 113 },
        { "key": "TYPE 2", "doc_count": 32 }
      ]
    }
  ],
  "signals": [
    {
      "name": "hoveredBar",
      "value": null,
      "on": [
        {"events": "rect:mouseover", "update": "datum.key"},
        {"events": "rect:mouseout", "update": "null"}
      ]
    },
    {
      "name": "hoveredText",
      "value": null,
      "on": [
        {"events": "text:mouseover", "update": "datum.key"},
        {"events": "text:mouseout", "update": "null"}
      ]
    },
    {
      "name": "hoveredEventType",
      "value": null,
      "on": [
        {"events": "rect:mouseover", "update": "datum"},
        {"events": "rect:mouseout", "update": "null"},
        {"events": "text:mouseover", "update": "datum"},
        {"events": "text:mouseout", "update": "null"}
      ]
    }
  ],
  "scales": [
    {
      "name": "x",
      "type": "band",
      "domain": {"data": "source", "field": "key"},
      "range": "width",
      "paddingInner": 0.1,
      "paddingOuter": 0.2
    },
    {
      "name": "y",
      "type": "linear",
      "domain": {"data": "source", "field": "doc_count"},
      "range": "height",
      "nice": true
    }
  ],
  "marks": [
    {
      "type": "rect",
      "from": {"data": "source"},
      "encode": {
        "enter": {
          "x": {"scale": "x", "field": "key"},
          "width": {"scale": "x", "band": 0.8},
          "y": {"scale": "y", "field": "doc_count"},
          "y2": {"scale": "y", "value": 0},
          "fill": {"value": "#00afa4"},
          "opacity": {"value": 1}
        },
        "update": {
          "opacity": [
            {"test": "datum.key === hoveredBar || datum.key === hoveredText", "value": 0.7},
            {"value": 1}
          ]
        }
      }
    },
    {
      "type": "text",
      "from": {"data": "source"},
      "encode": {
        "enter": {
          "x": {"scale": "x", "field": "key", "band": 0.43},
          "y": {"scale": "y", "field": "doc_count", "offset": -10},
          "text": {"field": "key"},
          "align": {"value": "center"},
          "fill": {"value": "black"}
        },
        "update": {
          "fill": [
            {"test": "datum.key === hoveredBar || datum.key === hoveredText", "value": "#f05c3e"},
            {"value": "black"}
          ]
        }
      }
    },
    // Hovered EventType details mark
    {
      "type": "text",
      "encode": {
        "enter": {
          "x": {"signal": "width - 10"},
          "y": {"signal": "-150"},
          "text": {"signal": "hoveredEventType ? hoveredEventType.key + ': ' + hoveredEventType.doc_count : ''"},
          "align": {"value": "right"},
          "baseline": {"value": "bottom"},
          "fill": {"value": "#f05c3e"},
          "fontWeight": {"value": "bold"},
          "fontSize": {"value": 16}
        },
        "update": { 
          "fillOpacity": {"signal": "hoveredEventType ? 1 : 0"}
        }
      }
    }
  ]
}

I am not sure if the signal "hoveredEventType", which is supposed to receive data during hover and mouseout events, is correctly defined, since it never gets to be triggered. In comparison, the other signals - "hoveredBar" and "hoveredText" - work as expected updating the bar / related text marks.

Is my approach wrong, or is there a limitation that prevents the behavior I intend to implement?


Solution

  • Is the following what you want?

    {
      "$schema": "https://vega.github.io/schema/vega/v5.json",
      "description": "The PM2.5 value of Beijing observed 15 keys, highlighting the keys when PM2.5 level is hazardous to human health. Data source https://chartaccent.github.io/chartaccent.html",
      "background": "white",
      "padding": 5,
      "height": 250,
      "width": 500,
      "autosize": {"type": "fit", "resize": true},
      "data": [
        {
          "name": "source",
          "values": [
            {"key": "TYPE 1", "doc_count": 113},
            {"key": "TYPE 2", "doc_count": 32}
          ]
        }
      ],
      "signals": [
        {
          "name": "hoveredBar",
          "value": null,
          "on": [
            {"events": "rect:mouseover", "update": "datum.key"},
            {"events": "rect:mouseout", "update": "null"}
          ]
        },
        {
          "name": "hoveredText",
          "value": null,
          "on": [
            {"events": "text:mouseover", "update": "datum.key"},
            {"events": "text:mouseout", "update": "null"}
          ]
        },
        {
          "name": "hoveredEventType",
          "value": null,
          "on": [
            {"events": "rect:mouseover", "update": "datum"},
            {"events": "rect:mouseout", "update": "null"},
            {"events": "text:mouseover", "update": "datum"},
            {"events": "text:mouseout", "update": "null"}
          ]
        }
      ],
      "scales": [
        {
          "name": "x",
          "type": "band",
          "domain": {"data": "source", "field": "key"},
          "range": "width",
          "paddingInner": 0.1,
          "paddingOuter": 0.2
        },
        {
          "name": "y",
          "type": "linear",
          "domain": {"data": "source", "field": "doc_count"},
          "range": "height",
          "nice": true
        }
      ],
      "marks": [
        {
          "type": "rect",
          "from": {"data": "source"},
          "encode": {
            "enter": {
              "x": {"scale": "x", "field": "key"},
              "width": {"scale": "x", "band": 0.8},
              "y": {"scale": "y", "field": "doc_count"},
              "y2": {"scale": "y", "value": 0},
              "fill": {"value": "#00afa4"},
              "opacity": {"value": 1}
            },
            "update": {
              "opacity": [
                {
                  "test": "datum.key === hoveredBar || datum.key === hoveredText",
                  "value": 0.7
                },
                {"value": 1}
              ]
            }
          }
        },
        {
          "type": "text",
          "from": {"data": "source"},
          "encode": {
            "enter": {
              "x": {"scale": "x", "field": "key", "band": 0.43},
              "y": {"scale": "y", "field": "doc_count", "offset": -10},
              "text": {"field": "key"},
              "align": {"value": "center"},
              "fill": {"value": "black"}
            },
            "update": {
              "fill": [
                {
                  "test": "datum.key === hoveredBar || datum.key === hoveredText",
                  "value": "#f05c3e"
                },
                {"value": "black"}
              ]
            }
          }
        },
        {
          "type": "text",
          "data": [{}],
          "encode": {
            "update": {
              "x": {"signal": "width - 10"},
              "y": {"signal": "-150"},
              "text": {
                "signal": "hoveredEventType ? hoveredEventType.key + ': ' + hoveredEventType.doc_count : ''"
              },
              "align": {"value": "right"},
              "baseline": {"value": "bottom"},
              "fill": {"value": "#f05c3e"},
              "fontWeight": {"value": "bold"},
              "fontSize": {"value": 16}
            }
          }
        }
      ]
    }