You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

修改Highcharts组织结构图鼠标追踪:hover节点显示全部关联节点

实现Highcharts组织结构图多级关联节点鼠标追踪

要实现悬停节点时显示所有多级父节点和子节点,需要自定义鼠标事件处理逻辑,递归遍历并控制节点和连线的可见性。以下是修改后的完整代码:

修改后的JavaScript代码

Highcharts.chart('container', {
    chart: {
        height: 600,
        inverted: true
    },

    title: {
        text: 'Highcharts Org Chart'
    },

    accessibility: {
        point: {
            descriptionFormatter: function (point) {
                var nodeName = point.toNode.name,
                    nodeId = point.toNode.id,
                    nodeDesc = nodeName === nodeId ? nodeName : nodeName + ', ' + nodeId,
                    parentDesc = point.fromNode.id;
                return point.index + '. ' + nodeDesc + ', reports to ' + parentDesc + '.';
            }
        }
    },

    series: [{
        type: 'organization',
        name: 'Highsoft',
        keys: ['from', 'to'],
        data: [
            ['Shareholders', 'Board'],
            ['Board', 'CEO'],
            ['CEO', 'CTO'],
            ['CEO', 'CPO'],
            ['CEO', 'CSO'],
            ['CEO', 'HR'],
            ['CTO', 'Product'],
            ['CTO', 'Web'],
            ['CSO', 'Sales'],
            ['HR', 'Market'],
            ['CSO', 'Market'],
            ['HR', 'Market'],
            ['CTO', 'Market']
        ],
        levels: [{
            level: 0,
            color: 'silver',
            dataLabels: {
                color: 'black'
            },
            height: 25
        }, {
            level: 1,
            color: 'silver',
            dataLabels: {
                color: 'black'
            },
            height: 25
        }, {
            level: 2,
            color: '#980104'
        }, {
            level: 4,
            color: '#359154'
        }],
        nodes: [{
            id: 'Shareholders'
        }, {
            id: 'Board'
        }, {
            id: 'CEO',
            title: 'CEO',
            name: 'Atle Sivertsen',
            image: 'https://wp-assets.highcharts.com/www-highcharts-com/blog/wp-content/uploads/2022/06/30081411/portrett-sorthvitt.jpg'
        }, {
            id: 'HR',
            title: 'CFO',
            name: 'Anne Jorunn Fjærestad',
            color: '#007ad0',
            image: 'https://wp-assets.highcharts.com/www-highcharts-com/blog/wp-content/uploads/2020/03/17131210/Highsoft_04045_.jpg'
        }, {
            id: 'CTO',
            title: 'CTO',
            name: 'Christer Vasseng',
            image: 'https://wp-assets.highcharts.com/www-highcharts-com/blog/wp-content/uploads/2020/03/17131120/Highsoft_04074_.jpg'
        }, {
            id: 'CPO',
            title: 'CPO',
            name: 'Torstein Hønsi',
            image: 'https://wp-assets.highcharts.com/www-highcharts-com/blog/wp-content/uploads/2020/03/17131213/Highsoft_03998_.jpg'
        }, {
            id: 'CSO',
            title: 'CSO',
            name: 'Anita Nesse',
            image: 'https://wp-assets.highcharts.com/www-highcharts-com/blog/wp-content/uploads/2020/03/17131156/Highsoft_03834_.jpg'
        }, {
            id: 'Product',
            name: 'Product developers'
        }, {
            id: 'Web',
            name: 'Web devs, sys admin'
        }, {
            id: 'Sales',
            name: 'Sales team'
        }, {
            id: 'Market',
            name: 'Marketing team',
            column: 5
        }],
        colorByPoint: false,
        color: '#007ad0',
        dataLabels: {
            color: 'white'
        },
        borderColor: 'white',
        nodeWidth: 65,
        // 自定义鼠标事件
        point: {
            events: {
                mouseOver: function() {
                    const series = this.series;
                    const allNodes = series.nodeLookup;
                    const allPoints = series.points;
                    const targetNode = this.toNode || this.fromNode;

                    // 递归收集所有祖先节点
                    const getAncestors = (node) => {
                        const ancestors = [];
                        const parentPoints = allPoints.filter(p => p.toNode.id === node.id);
                        parentPoints.forEach(p => {
                            const parentNode = p.fromNode;
                            ancestors.push(parentNode);
                            ancestors.push(...getAncestors(parentNode));
                        });
                        return ancestors;
                    };

                    // 递归收集所有后代节点
                    const getDescendants = (node) => {
                        const descendants = [];
                        const childPoints = allPoints.filter(p => p.fromNode.id === node.id);
                        childPoints.forEach(p => {
                            const childNode = p.toNode;
                            descendants.push(childNode);
                            descendants.push(...getDescendants(childNode));
                        });
                        return descendants;
                    };

                    const relatedNodes = [targetNode, ...getAncestors(targetNode), ...getDescendants(targetNode)];
                    const relatedNodeIds = new Set(relatedNodes.map(n => n.id));

                    // 隐藏非关联节点和连线
                    Object.values(allNodes).forEach(node => {
                        node.graphic.attr('opacity', relatedNodeIds.has(node.id) ? 1 : 0.1);
                        if (node.dataLabel) {
                            node.dataLabel.attr('opacity', relatedNodeIds.has(node.id) ? 1 : 0);
                        }
                    });

                    allPoints.forEach(point => {
                        point.graphic.attr('opacity', 
                            relatedNodeIds.has(point.fromNode.id) && relatedNodeIds.has(point.toNode.id) ? 1 : 0.1
                        );
                    });
                },
                mouseOut: function() {
                    const series = this.series;
                    const allNodes = series.nodeLookup;
                    const allPoints = series.points;

                    // 恢复所有节点和连线可见
                    Object.values(allNodes).forEach(node => {
                        node.graphic.attr('opacity', 1);
                        if (node.dataLabel) {
                            node.dataLabel.attr('opacity', 1);
                        }
                    });

                    allPoints.forEach(point => {
                        point.graphic.attr('opacity', 1);
                    });
                }
            }
        }
    }],
    tooltip: {
        outside: false
    },
    exporting: {
        allowHTML: true,
        sourceWidth: 800,
        sourceHeight: 600
    }
});

核心逻辑说明

  1. 递归收集关联节点:
    • getAncestors函数向上遍历所有父级节点,直到根节点
    • getDescendants函数向下遍历所有子级节点,直到叶子节点
  2. 控制可见性:
    • 鼠标悬停时,将非关联节点和连线设置为半透明(opacity:0.1),隐藏非关联节点的标签
    • 鼠标移出时,恢复所有元素的完全可见状态

CSS和HTML代码保持原代码不变即可。

内容的提问来源于stack exchange,提问作者elina

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 14:05:03