Search

Tooltips on Items

Interactions

Use tooltips to display useful information about nodes and links.

Tooltips on Items
View live example →

Tooltips are a simple mechanism to give the user more information about chart items. There are two basic ways of populating tooltips: pre-loading and dynamic loading.

Preloading loads the data onto the d property for all nodes up front, which is why it's mostly convenient only for relatively small datasets. Preloading is used in this demo.

Dynamic loading brings the data in dynamically when each node is hovered, which makes it suitable even for very large datasets.

Object format used:

Key function used:

import KeyLines from "keylines";
import { data } from "./data.js";

// declaring global vars
let chart;

const nodeBaseSize = 26;
const tooltipContainer = document.querySelector("#tooltip-container");
const tooltip = {
  itemId: null,
  element: null,
};

// Create the HTML code that is going to fill the tooltip
const templateHtml = document.getElementById("tt_html").innerHTML;

function updateTooltipPosition() {
  if (tooltip.itemId) {
    const { itemId, element } = tooltip;
    const item = chart.getItem(itemId);
    const coordinates = chart.viewCoordinates(item.x, item.y);
    const x = coordinates.x;
    const y = coordinates.y;
    const zoom = chart.viewOptions().zoom;
    const arrowTipOffset = 20;
    // get the size of the node on screen
    const nodeSize = nodeBaseSize * (item.e || 1) * zoom;
    element.style.opacity = 0;
    // allow fade in and out to animate
    element.style.transition = "opacity 0.3s ease";
    // scale the size of the tooltip depending on zoom level
    element.style.transform = `scale(${Math.max(0.75, Math.min(2, zoom))}`;
    const top = y - element.clientHeight / 2;
    element.style.left = `${x + arrowTipOffset + nodeSize}px`;
    element.style.top = `${top}px`;
    element.style.opacity = 1;
  }
}

function closeTooltip() {
  if (tooltip.element) {
    tooltip.element.style.opacity = 0;
    tooltip.itemId = null;
    tooltip.element = null;
  }
}

function handleTooltip({ id }) {
  const item = chart.getItem(id);
  if (item && item.type === "node") {
    const html = templateHtml
      .replace(/{{label}}/, item.t)
      .replace(/{{gender}}/, item.d.g)
      .replace(/{{name}}/, item.d.fn)
      .replace(/{{surname}}/, item.d.ln);

    // Add it to the DOM
    tooltipContainer.innerHTML = html;
    tooltip.element = document.getElementById("tooltip");
    tooltip.itemId = id;
    updateTooltipPosition();
  } else if (tooltip.element) {
    closeTooltip();
  }
}

function chartLoaded() {
  let { width, height } = chart.viewOptions();
  chart.on("view-change", () => {
    const { width: newWidth, height: newHeight } = chart.viewOptions();
    if (width !== newWidth || height !== newHeight) {
      closeTooltip();
      width = newWidth;
      height = newHeight;
    }
    updateTooltipPosition();
  });
  chart.on("drag-move", updateTooltipPosition);
  chart.on("hover", handleTooltip);

  chart.load(data);
  chart.layout();
}

async function startKeyLines() {
  const options = {
    drag: {
      links: false,
    },
    logo: { u: "/images/Logo.png" },
    hover: 100,
    iconFontFamily: "Font Awesome 5 Free",
    imageAlignment: { ["far fa-user"]: { dy: -5, e: 0.9 } },
  };

  chart = await KeyLines.create({
    container: "klchart",
    options,
  });
  chartLoaded();
}

function loadKeyLines() {
  document.fonts.load('24px "Font Awesome 5 Free"').then(startKeyLines);
}

window.addEventListener("DOMContentLoaded", loadKeyLines);
import KeyLines from "keylines";
export const data = {
  type: "LinkChart",
  items: [
    {
      id: "ab",
      id1: "a",
      id2: "b",
      type: "link",
      t: "Associate",
      off: 0,
    },
    {
      id: "ac",
      id1: "a",
      id2: "c",
      type: "link",
      t: "Associate",
      off: 0,
    },
    {
      id: "ad",
      id1: "a",
      id2: "d",
      type: "link",
      t: "Associate",
      off: 0,
    },
    {
      id: "ae",
      id1: "a",
      id2: "e",
      type: "link",
      t: "Associate",
      off: 0,
    },
    {
      id: "bc",
      id1: "b",
      id2: "c",
      type: "link",
      t: "Associate",
      off: 0,
    },
    {
      id: "bd",
      id1: "b",
      id2: "d",
      type: "link",
      t: "Associate",
      off: 0,
    },
    {
      id: "cj",
      id1: "c",
      id2: "j",
      type: "link",
      t: "Associate",
      off: 0,
    },
    {
      id: "ci",
      id1: "c",
      id2: "i",
      type: "link",
      t: "Associate",
      off: 0,
    },
    {
      id: "eg",
      id1: "e",
      id2: "g",
      type: "link",
      t: "Associate",
      off: 20,
    },
    {
      id: "hi",
      id1: "h",
      id2: "i",
      type: "link",
      t: "Associate",
      off: 0,
    },
    {
      id: "ge",
      id1: "g",
      id2: "e",
      type: "link",
      t: "Associate",
      off: 20,
    },
    {
      id: "ij",
      id1: "i",
      id2: "j",
      type: "link",
      t: "Associate",
      off: 0,
    },
    {
      id: "a",
      type: "node",
      t: "Bob",
      c: "#0155A5",
      fi: {
        t: "far fa-user",
        c: "white",
      },
      d: {
        fn: "Robert",
        ln: "Smith",
        g: "Male",
      },
    },
    {
      id: "b",
      type: "node",
      t: "Diane",
      c: "#B0006E",
      fi: {
        t: "far fa-user",
        c: "white",
      },
      d: {
        fn: "Diane",
        ln: "Caliva",
        g: "Female",
      },
    },
    {
      id: "c",
      type: "node",
      t: "Debbie",
      c: "#B0006E",
      fi: {
        t: "far fa-user",
        c: "white",
      },
      d: {
        fn: "Debbie",
        ln: "Denise",
        g: "Female",
      },
    },
    {
      id: "d",
      type: "node",
      t: "Sean",
      c: "#B0006E",
      fi: {
        t: "far fa-user",
        c: "white",
      },
      d: {
        fn: "Sean",
        ln: "Young",
        g: "Female",
      },
    },
    {
      id: "e",
      type: "node",
      t: "Tim",
      c: "#0155A5",
      fi: {
        t: "far fa-user",
        c: "white",
      },
      d: {
        fn: "Tim",
        ln: "Angulo",
        g: "Male",
      },
    },
    {
      id: "f",
      type: "node",
      t: "Bud",
      c: "#0155A5",
      fi: {
        t: "far fa-user",
        c: "white",
      },
      d: {
        fn: "Bud",
        ln: "Alper",
        g: "Male",
      },
    },
    {
      id: "g",
      type: "node",
      t: "Morris",
      c: "#0155A5",
      fi: {
        t: "far fa-user",
        c: "white",
      },
      d: {
        fn: "Morris",
        ln: "Chapnick",
        g: "Male",
      },
    },
    {
      id: "h",
      type: "node",
      t: "Joe",
      c: "#0155A5",
      fi: {
        t: "far fa-user",
        c: "white",
      },
      d: {
        fn: "Joe",
        ln: "Turkel",
        g: "Male",
      },
    },
    {
      id: "j",
      type: "node",
      t: "David",
      c: "#0155A5",
      fi: {
        t: "far fa-user",
        c: "white",
      },
      d: {
        fn: "David",
        ln: "Snyder",
        g: "Male",
      },
    },
    {
      id: "i",
      type: "node",
      t: "Michael",
      c: "#0155A5",
      fi: {
        t: "far fa-user",
        c: "white",
      },
      d: {
        fn: "Michael",
        ln: "Deeley",
        g: "Male",
      },
    },
  ],
  combos: {
    nodes: [],
    links: [],
  },
  viewSettings: {
    width: 768,
    height: 565,
    zoom: 0.8185140073081608,
    offsetX: 729.4129110840438,
    offsetY: 527.2356881851401,
  },
};
<!doctype html>
<html lang="en" style="background-color: #2d383f">
  <head>
    <meta charset="utf-8" />
    <title>Tooltips on Items</title>
    <link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/keylines.css" />
    <link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/minimalsdk.css" />
    <link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/sdk-layout.css" />
    <link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/demo.css" />
    <link
      rel="stylesheet"
      type="text/css"
      href="@fortawesome/[email protected]/css/fontawesome.css"
    />
    <link
      rel="stylesheet"
      type="text/css"
      href="@fortawesome/[email protected]/css/regular.css"
    />
    <link rel="stylesheet" href="./style.css" />
  </head>
  <body>
    <div id="klchart" class="klchart">
      <div id="tooltip-container"></div>
    </div>
    <template id="tt_html"
      ><div
        class="popover right"
        id="tooltip"
        style="
          margin: 0px;
          transform-origin: left;
          position: absolute;
          min-width: 70px;
          pointer-events: none;
        "
      >
        <div class="arrow"></div>
        <h2 class="popover-title"><strong>{{label}}</strong></h2>
        <div class="popover-content">
          <table class="table-condensed">
            <tbody>
              <tr>
                <td style="text-align: right"><strong>Gender</strong></td>
                <td>{{gender}}</td>
              </tr>
              <tr>
                <td style="text-align: right"><strong>Name</strong></td>
                <td>{{name}}</td>
              </tr>
              <tr>
                <td style="text-align: right"><strong>Surname</strong></td>
                <td>{{surname}}</td>
              </tr>
            </tbody>
          </table>
        </div>
      </div></template
    >
    <script type="module" src="./code.js"></script>
  </body>
</html>
#htmlModal {
  -webkit-user-select: text;
  -khtml-user-select: text;
  -moz-user-select: text;
  -ms-user-select: text;
  user-select: text;
}

/* Tooltip styling */
.popover {
  z-index: 1000;
  background: white;
}
:fullscreen .popover {
  margin-top: -70px;
}
#tooltip .popover-title {
  font-size: 16px;
  padding: 8px 14px;
  margin: 0px;
  line-height: 20px;
  top: 0px;
}
#tooltip .popover-content {
  font-size: 12px;
  border: 1px solid gray;
}
#tooltip .popover-content .td {
  border: 1px solid gray;
  max-height: 20px;
}
#tooltip .arrow {
  background-color: white;
  border-bottom: 1px solid gray;
  border-left: 1px solid gray;
  transform: translate(-5px, 60px) rotateZ(45deg);
  width: 10px;
  height: 10px;
  position: absolute;
}

.popover-content {
  padding: 9px 14px;
}

.klchart {
  overflow: hidden;
}

#tooltip {
  background: white;
  border: black;
  transition: opacity 0.3s ease;
}

Terms of use

These terms do not alter or supersede any existing agreements between you (or your employer) and us.

By accessing or using any Content you agree to be bound by these Terms of Use. Please review these terms carefully before using the website.

The contents of this website, including but not limited to any text, code samples, API references, schemas, interactive tools, and other materials (collectively, the 'Content'), are made available for informational and internal evaluation purposes only. All intellectual property rights in the Content are reserved. No licence is granted to use the Content for any commercial purpose, or to copy, distribute, modify, reverse-engineer, or incorporate any part of the Content into any product or service, without our prior written consent.

This Content is provided “as is” and “as available,” without any representations, warranties, or guarantees of any kind, whether express or implied, including but not limited to implied warranties of merchantability, fitness for a particular purpose, non-infringement, or accuracy. To the fullest extent permitted by applicable law, we expressly exclude and disclaim all implied warranties, conditions, and other terms that might otherwise be implied.

We disclaim all liability for any loss or damage, whether direct, indirect, incidental, consequential, or otherwise, arising from any reliance placed on the Content or from your use of it, to the fullest extent permitted by applicable law. By continuing to access or use the Content, you acknowledge and agree to these terms.