Kim
Kim

Reputation: 385

How to detect if the text is overflow(text-overflow: ellipsis) in angular controller

Is there any way we can detect if the text is overflow in angular controller?

In my CSS I have the following code:

width: calc(100% - 60px);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding-top: 5px;

And I want to be able to detect if the text is overflow in the angular controller so I can display the tooltip for it. If the text is not overflow then no need to display the tooltip, which was the reason why I want to be able to detect if the text is overflow in the controller.

Upvotes: 13

Views: 32079

Answers (4)

JimmyTheCode
JimmyTheCode

Reputation: 5694

Spent half a day working on this...

There's a codepen solution that uses vanilla/alpine js. I've managed to adapt this to Angular:

import { CommonModule } from '@angular/common';
import { Component, ElementRef, Input, ViewChild } from '@angular/core';

@Component({
  selector: 'app-expand-if-ellipsis',
  templateUrl: './expand-if-ellipsis.component.html',
  styleUrl: './expand-if-ellipsis.component.css',
  standalone: true,
  imports: [CommonModule],
})
export class ExpandIfEllipsisComponent {
  @Input() pslText: string = "";
  @ViewChild('textEl') textEl: ElementRef|undefined;
  
  expanded: boolean = false;
  ellipsis: boolean = false;
  
  ngAfterViewInit(): void {
    setTimeout(()=>{
      this.setTruncate();
      this.expanded = false;
    }, 1)
  }
    
  toggleExpanded(expanded: boolean){
    this.expanded = !expanded;
  }
  
  setTruncate() {
    const element = this.textEl?.nativeElement;
    if(!element) return;
    if (
      element.offsetHeight < element.scrollHeight ||
      element.offsetWidth < element.scrollWidth
    ) {
      this.ellipsis = true;
    } else {
      this.ellipsis = false;
    }
  }

}

<div>
  <span 
    #textEl
    [class.ellipsis-4]="!expanded"
    >{{pslText}}</span>

  <div>
    <button
      *ngIf="ellipsis"
      class="button-to-link text--small"
      (click)="toggleExpanded(expanded)"
      >{{expanded ? 'Minimise comment' : 'Show full comment'}}</button>
  </div>
</div>
.ellipsis-4 {
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 4;
}

I've tried to create a demo on Stackblitz, but I can't get the darned thing to work with a super simple app. If someone could fix it for me I'd appreciate it. This solution works on my localhost, but some frustrating bugs on stackblitz that I can't figure out.

Upvotes: 0

Ria Pacheco
Ria Pacheco

Reputation: 161

Here's a bit of a cleaner solution I've found for this use-case. Instead of CSS, I used the slice angular pipe, where the two values mark the start and end of the sliced characters.

Angular Ellipsis with Slice and *ngIf

  • Slice pipe isolates first 20 characters
  • A span follows with a manually written "..." string
  • The ellipsis span is conditional on how many characters are showing using item.name.length and having it either be greater or equal to the number of characters allowed from the slice
<a *ngFor="let item of items">
  {{ item.name | slice: 0 : 20 }}<span *ngIf="item.name.length >=20">...</span>
</a>

Show Tooltip Conditionally

I also wanted this to display a tooltip. But I didn't want any other anchors to have a tooltip unless the ellipsis was present (since the tooltip would show the entire name)

Template
  • With mouseover, uses a ternary operator that triggers an onHover method IF it has equal or more characters than previous identified
  • The onHover method takes in both the $event and the name (string) of the item
  • Tooltip has styling required to appear at mouse coords (absolute) but binds component variables to [style.top] and [style.left] so it appears where mouse event fires
  • The (mouseout) ensures the tooltip is gone when no longer hovering over the element
<a
  *ngFor="let item of items"
  (mouseover)="(item.name.length >= 20) ? onHover($event, item.name) : '' "
  (mouseout)="showsTooltip ? (showsTooltip = !showsTooltip) : '' ">
  {{ item.name | slice: 0 : 20 }}<span *ngIf="item.name.length >=20">...</span>
</a>

<!-- some other code -->

<div
  *ngIf="showsTooltip"
  [style.top]="tooltipTopY"
  [style.left]="tooltipLeftX"
  class="tooltip"
  >
  {{ tooltipText }}
</div>
Component
  • Catches the coordinates from the event object and binds them accordingly
export class SomeComponent implements OnInit {
  showsTooltip = false;
  tooltipText = '';
  tooltipTopY!: any;
  tooltipLeftX!: any;

  // ... other code

  onHover(e: any, cardName: string): void {
    this.tooltipText = cardName;
    if (e) {
      this.showsTooltip = true;
      this.tooltipTopY = e.clientY + 'px';
      this.tooltipLeftX = e.clientX + 'px';
    }
  }
}

Hope this helps! Grateful for thorough answers that've helped me here; and I find this solution has been pretty clutch for me so far - so just hoping to share where I can!

Upvotes: 2

NKS
NKS

Reputation: 43

In addition to Mathew's answer.

I applied that JavaScript logic to an angular 2+ implementation using material components. In this example, you can see that the same JS check is utilized to either disable or enable the Material Tooltip if the text was truncated.

<mat-expansion-panel-header 
            [matTooltipDisabled]="(titleContent.offsetWidth < matpanelTitle.scrollWidth)" 
            matTooltip="{{recording.alias}}">

    <mat-panel-title #matpanelTitle style="white-space: nowrap; 
                                           overflow: hidden; 
                                           text-overflow: ellipsis; 
                                           display: block;">

         <span #titleContent>
              {{recording.alias}}
         </span>

     </mat-panel-title>

</mat-expansion-panel-header>

Upvotes: 3

Mathew Berg
Mathew Berg

Reputation: 28750

There is no way for angular (or javascript in general) to know whether an element has used the "...". See this very similar question: HTML text-overflow ellipsis detection.

The best you can do is something like this (where you pass the element you care about in):

 function isEllipsisActive(e) {
      return (e.offsetWidth < e.scrollWidth);
 }

Js courtesy that link.

Upvotes: 20

Related Questions