Hover multi line tree node in Angular TreeView component

27 Aug 202513 minutes to read

When working with TreeView nodes that contain multi-line content, the default hover and selection behaviors may not align properly with the actual content height. This guide demonstrates how to create consistent hover and selection effects that cover the entire multi-line node content area.

Overview

Multi-line TreeView nodes present a unique challenge where the hover area (e-fullrow element) needs to match the actual content height (e-text-content element). Without proper height synchronization, users may experience inconsistent hover effects or selection areas that don’t cover the complete node content.

This implementation uses the TreeView component’s created and nodeSelecting events to dynamically adjust row heights and ensure proper hover behavior across all multi-line nodes.

Implementation

The solution involves two key components:

  1. Event handlers that calculate and apply proper heights to hover elements
  2. CSS styling that accommodates multi-line content layout

Component Setup

Configure the TreeView component with the necessary event handlers and styling:

import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { FormsModule } from '@angular/forms'
import { TreeViewModule } from '@syncfusion/ej2-angular-navigations'
import { Component, Inject, ViewChild } from '@angular/core';
import { TreeViewComponent } from '@syncfusion/ej2-angular-navigations';
import { NodeSelectEventArgs } from '@syncfusion/ej2-navigations';
/**
 * Hovering multiple line treeview
 */
@Component({
    imports: [
        FormsModule, TreeViewModule
    ],
    standalone: true,
    selector: 'app-container',
    template: `<div id='treeparent'><ejs-treeview id='treeElement' #treevalidate [fields]='field'  (nodeSelecting)='onSelect($event)' cssClass="customTree" (created)="onCreate($event)"></ejs-treeview></div>`
})
export class AppComponent {

    // Data source for TreeView component
    public hierarchicalData: Object[] = [
        {
            id: 1, name: 'Web Control sWeb ControlsWeb ControlsWeb ControlsWeb ControlsWeb ControlsWeb ControlsWeb Controls', expanded: true,
            child: [
                {
                    id: 2, name: 'CalendarCalendarCalendarCalendarCalendarCalendarCalendarCalendarCalendarCalendarCalendarCalendarCalendar', child: [
                        { id: 7, name: 'Constructors' },
                        { id: 8, name: 'Properties' },
                        { id: 9, name: 'Methods' },
                        { id: 10, name: 'Events' }
                    ]
                },
                {
                    id: 3, name: 'Data Grid', child: [
                        { id: 11, name: 'Constructors' },
                        { id: 12, name: 'Fields' },
                        { id: 13, name: 'Properties' },
                        { id: 14, name: 'Methods' },
                        { id: 15, name: 'Events' }
                    ]
                },
                {
                    id: 4, name: 'DropDownList', child: [
                        { id: 16, name: 'Constructors' },
                        { id: 17, name: 'Properties' },
                        { id: 18, name: 'Methods' }
                    ]
                },
                {
                    id: 5, name: 'Menu', child: [
                        { id: 19, name: 'Constructors' },
                        { id: 20, name: 'Fields' },
                        { id: 21, name: 'Properties' },
                        { id: 22, name: 'Methods' },
                        { id: 23, name: 'Events' }
                    ]
                }
            ]
        },
        {
            id: 24, name: 'Web Controls',
            child: [
                {
                    id: 25, name: 'Calendar', child: [
                        { id: 26, name: 'Constructors' },
                        { id: 27, name: 'Properties' },
                        { id: 28, name: 'Methods' },
                        { id: 29, name: 'Events' }
                    ]
                },
                {
                    id: 30, name: 'Data Grid', child: [
                        { id: 31, name: 'Constructors' },
                        { id: 32, name: 'Fields' },
                        { id: 33, name: 'Properties' },
                        { id: 34, name: 'Methods' },
                        { id: 35, name: 'Events' }
                    ]
                }
            ]
        }
    ];

    public field: Object = { dataSource: this.hierarchicalData, id: 'id', text: 'name', child: 'child' };

    @ViewChild('treevalidate') tree?: TreeViewComponent;

    // Triggers on node selection
    public onSelect(args: NodeSelectEventArgs): void {
        this.setHeight(args.node);
    }
    public onCreate(args: any) {
        // Triggers on mouse hover/keydown event
        ['mouseover', 'keydown'].forEach(evt =>
            this.tree?.element.addEventListener(evt, (event) => { this.setHeight(event.target); }));
    }


    // Sets e-fullrow to be the same as e-text-content
    public setHeight(element: any) {
        if (this.tree?.fullRowSelect) {
            if (element?.classList.contains("e-treeview")) {
                element = element.querySelector(".e-node-focus").querySelector(".e-fullrow");
            }
            else if (element.classList.contains("e-list-parent")) {
                element = element.querySelector(".e-fullrow");
            }
            else if (element.classList.value != ("e-fullrow") && element.closest(".e-list-item")) {
                element = element.closest(".e-list-item").querySelector(".e-fullrow");
            }
            if (element.nextElementSibling)
                element.style.height = element.nextElementSibling.offsetHeight + "px";
        }
    }
}
@import 'node_modules/@syncfusion/ej2-base/styles/material.css';
@import 'node_modules/@syncfusion/ej2-inputs/styles/material.css';
@import 'node_modules/@syncfusion/ej2-buttons/styles/material.css';
@import 'node_modules/@syncfusion/ej2-angular-base/styles/material.css';
@import 'node_modules/@syncfusion/ej2-angular-navigations/styles/material.css';
@import 'node_modules/@syncfusion/ej2-angular-inputs/styles/material.css';

#treeparent {
    display: block;
    max-width: 400px;
    max-height: 320px;
    margin: auto;
    overflow: auto;
    border: 1px solid #dddddd;
    border-radius: 3px;
}

.customTree li .e-list-text{
    white-space: normal;
    word-break: break-all;
  }
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));