Tooltip on a ForeignObject
If your nodes use foreignobject, you can still attach fully custom SVG tooltips and animate them on hover.
This example combines three parts:
- A custom
foreignobjectfield with SVG content. - Tooltip templates rendered in the chart
renderevent. - Hover handlers attached on
onRedrawto animate show/hide.
Example
javascript
let drive = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48px" height="48px">
<path fill="#1e88e5" d="M38.59,39c-0.535,0.93-0.298,1.68-1.195,2.197C36.498,41.715,35.465,42,34.39,42H13.61 c-1.074,0-2.106-0.285-3.004-0.802C9.708,40.681,9.945,39.93,9.41,39l7.67-9h13.84L38.59,39z"/>
<path fill="#fbc02d" d="M27.463,6.999c1.073-0.002,2.104-0.716,3.001-0.198c0.897,0.519,1.66,1.27,2.197,2.201l10.39,17.996 c0.537,0.93,0.807,1.967,0.808,3.002c0.001,1.037-1.267,2.073-1.806,3.001l-11.127-3.005l-6.924-11.993L27.463,6.999z"/>
<path fill="#e53935" d="M43.86,30c0,1.04-0.27,2.07-0.81,3l-3.67,6.35c-0.53,0.78-1.21,1.4-1.99,1.85L30.92,30H43.86z"/>
<path fill="#4caf50" d="M5.947,33.001c-0.538-0.928-1.806-1.964-1.806-3c0.001-1.036,0.27-2.073,0.808-3.004l10.39-17.996 c0.537-0.93,1.3-1.682,2.196-2.2c0.897-0.519,1.929,0.195,3.002,0.197l3.459,11.009l-6.922,11.989L5.947,33.001z"/>
<path fill="#1565c0" d="M17.08,30l-6.47,11.2c-0.78-0.45-1.46-1.07-1.99-1.85L4.95,33c-0.54-0.93-0.81-1.96-0.81-3H17.08z"/>
<path fill="#2e7d32" d="M30.46,6.8L24,18L17.53,6.8c0.78-0.45,1.66-0.73,2.6-0.79L27.46,6C28.54,6,29.57,6.28,30.46,6.8z"/>
</svg>`;
let tooltip = `
<g data-t-id="{id}" transform="matrix(0.001,0,0,0.001,{x},{y})">
<path stroke="#FFCA28" fill="#fff" d="M 0,0 L -10,-10 H -85 Q -90,-10 -90,-15 V -65 Q -90,-70 -85,-70 H 85 Q 90,-70 90,-65 V -15 Q 90,-10 85,-10 H 10 L 0,0 z"></path>
{text}
</g>`;
let tooltipText = `
<text text-anchor="middle" data-width="130" fill="#F57C00" x="0" y="-32">{val}</text>`;
OrgChart.templates.ana.html =
'<foreignobject class="node" x="20" y="10" width="200" height="100">' +
drive +
'{val}</foreignobject>';
let chart = new OrgChart(document.getElementById("tree"), {
mouseScroll: OrgChart.action.none,
nodeMouseClick: OrgChart.action.none,
template: "ana",
enableSearch: false,
nodeBinding: {
field_0: "name",
html: "html",
tooltip: "tooltip"
},
nodeMenu: {
details: { text: "Details" },
edit: { text: "Edit" },
add: { text: "Add" },
remove: { text: "Remove" }
}
});
function showTooltip(id) {
let tooltipElement = document.querySelector('[data-t-id="' + id + '"]');
if (!tooltipElement) return;
let transformStart = OrgChart._getTransform(tooltipElement);
let transformEnd = transformStart.slice(0);
transformEnd[0] = 1;
transformEnd[3] = 1;
OrgChart.animate(
tooltipElement,
{ transform: transformStart },
{ transform: transformEnd },
300,
OrgChart.anim.outBack
);
}
function hideTooltip(id) {
let tooltipElement = document.querySelector('[data-t-id="' + id + '"]');
if (!tooltipElement) return;
let transformStart = OrgChart._getTransform(tooltipElement);
let transformEnd = transformStart.slice(0);
transformEnd[0] = 0.001;
transformEnd[3] = 0.001;
OrgChart.animate(
tooltipElement,
{ transform: transformStart },
{ transform: transformEnd },
300,
OrgChart.anim.inBack
);
}
chart.onRedraw(() => {
let fieldElements = chart.element.querySelectorAll('[data-n-id] foreignobject svg');
for (let i = 0; i < fieldElements.length; i++) {
let fieldElement = fieldElements[i];
fieldElement.onmouseenter = function () {
let id = this.closest('[data-n-id]').getAttribute('data-n-id');
showTooltip(id);
};
fieldElement.onmouseleave = function () {
let id = this.closest('[data-n-id]').getAttribute('data-n-id');
hideTooltip(id);
};
}
});
chart.on("render", function (chart, args) {
for (let i = 0; i < args.res.visibleNodeIds.length; i++) {
let node = chart.getNode(args.res.visibleNodeIds[i]);
let data = chart.get(node.id);
if (data.tooltip) {
args.content += tooltip
.replace("{x}", node.x + node.w / 2)
.replace("{y}", node.y + 5)
.replace("{id}", node.id)
.replace(
"{text}",
tooltipText.replace(
"{val}",
OrgChart.wrapText(data.tooltip, tooltipText)
)
);
}
}
});
chart.load([
{
id: "1",
html: "<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi id pellentesque lacus.</div>",
tooltip: "1 my fancy tooltip"
},
{
id: "2",
pid: "1",
html: "<div>Mauris aliquam magna sapien. Ut a diam ac arcu commodo maximus.</div>",
tooltip: "2 my fancy tooltip"
},
{
id: "3",
pid: "1",
html: "<div>Phasellus eros felis, pellentesque quis ultrices nec, tempus ac felis.</div>",
tooltip: "3 my fancy tooltip"
}
]);We use the following CSS to fix text rendering in this example:
css
.light {
font: 13px Helvetica,"Segoe UI",Arial,sans-serif;
}
foreignObject {
font: 13px Helvetica;
}Key Points
- Render tooltip markup in
chart.on("render")so each visible node gets the correct position. - Use a small scale matrix (
0.001) as hidden state and animate to scale1on hover. - Attach hover listeners in
onRedrawbecause node DOM can be rebuilt after interactions.