Article image

Journey to Frontend Jungle | ToC

A long time ago in a galaxy far far away...

As a backend engineer, my primary focus has always been on developing robust and efficient systems that power web applications.

However, recently I decided to make blog as my pet project where I developed a table of contents (TOC) component for an Angular application.

This experience opened up a whole new world of possibilities and challenges that I had never encountered before.

In this article, I will share my journey of developing the TOC component and how it sparked my interest in frontend development.

In our quest to create an engaging TOC component, we will harness some of Angular's most powerful features, such as ViewChild, ElementRef, and dependency injection. 

Let's explore the main sections of our TOC component: constructor, ngAfterViewInit, and IntersectionObserver Callback.

The constructor sets the stage for our TOC component. 

constructor(@Inject(DOCUMENT) private document: Document) {
  // ...
}

 

We also initialize the IntersectionObserver, a key player in detecting when an element enters or exits the viewport.

This functionality is crucial for updating the TOC as users scroll through the content.

ngAfterViewInit: Lifecycle Hook and DOM Manipulation

The ngAfterViewInit method is an Angular lifecycle hook that gets called after the view is initialized.

At this point, the DOM elements are rendered and ready for manipulation.
 In this method, we first iterate through the tocTargetElementRef  array, which holds the heading elements.

For each heading, we create a new TocElement object and push it to the tocList array. 

this.tocTargetElementRef.forEach((el) => {
  this.tocList?.push({
    id: el.id,
    lvl: parseInt(el.localName.charAt(1)),
    name: el.textContent ?? ''
  });
});

Next, we iterate through all elements with an id attribute within the toc-target div, observing them using the IntersectionObserver instance.

This process ensures that we can monitor the headings as they enter and exit the viewport.

this.document.querySelectorAll('div#toc-target *[id]').forEach((section) => {
  this.intersectionObserver?.observe(section);
});

IntersectionObserver Callback: Dynamic TOC Updates


The IntersectionObserver callback function is where the magic happens is triggered when the observed elements intersect with the viewport, this callback allows us to dynamically update the TOC in response to user interactions.


 1. We begin by iterating through the entries and checking if they intersect with the viewport.

 2. If an entry is intersecting, we add the toc-active class to the corresponding TOC element. If not, we remove the toc-active class.

 3. We then identify the first and last intersecting elements and calculate their positions.

 4. Finally, we update the SVG path element's d attribute to connect the active TOC items, adding a visually appealing touch to our component.

 

const intersectionObserverCallback = (entries) => {
  entries.forEach((el) => {
    const tocElement = this.document.getElementById('toc-' + el.target.id);
    if (el.isIntersecting) {
      tocElement?.classList.add('toc-active');
    } else {
      tocElement?.classList.remove('toc-active');
    }
  });

  const intersected = this.document.getElementsByClassName('toc-active');
  if (intersected.length === 0) return;
  const firstRect = (intersected[0] as HTMLElement).getBoundingClientRect();
  const lastRect = (
    intersected[intersected.length - 1] as HTMLElement
  ).getBoundingClientRect();

  const d = `M ${firstRect.left} ${firstRect.top + firstRect.height - 25} L ${
    lastRect.left
  } ${lastRect.top + lastRect.height}`;

  this.tocPathEl.nativeElement.setAttribute('d', d);
};

Conclusion

By leveraging Angular's powerful features, we've crafted a dynamic and interactive TOC component that listens for viewport changes and updates the TOC items accordingly. 

In conclusion, my foray into frontend development through the creation of a table of contents component was nothing short of eye-opening.

As a backend engineer, I had never fully appreciated the intricacies and nuances of designing and implementing user interfaces.

However, this project challenged me to think outside of my comfort zone and explore new avenues of programming.

Through this experience, I discovered that frontend development is just as exciting and rewarding as backend development.

 Who knows what other surprises and opportunities await me in the world of programming? One thing is for sure - I am ready to embrace them with open arms.

View comments