Using TypeScript to set an HTMLElement in a web page

Currently in the process of transitioning my old JavaScript code to TypeScript. One of the components I have is a Table Of Contents JSX component that helps me navigate easily to specific headings.

I had a function that calculated the total offset needed for scrolling to a particular heading. However, after converting it to a '.tsx' file, an error popped up:

The error reads: 'Type 'Element' is missing the following properties from type 'HTMLElement': accessKey, accessKeyLabel, autocapitalize, dir, and 107 more.'

// TOC.tsx
...
const accumulateOffsetTop = ( el: HTMLElement | null, totalOffset = 0 ) => {
  while ( el ) {
    totalOffset += el.offsetTop - el.scrollTop + el.clientTop
    el = el.offsetParent // <- Error occurs here
  }
  return totalOffset
}
...

This function gets called at this location:

// TOC.tsx
...
export default function Toc(
  { headingSelector, getTitle, getDepth, ...rest }
) {
  const { throttleTime = 200, tocTitle = `Contents` } = rest
  const [headings, setHeadings] = useState( {
    titles: [],
    nodes: [],
    minDepth: 0,
    offsets: [],
  } )
  const [open, setOpen] = useState( false )
  const [active, setActive] = useState()
  const ref = useRef()
  useOnClickOutside( ref, () => setOpen( false ) )
  useEffect( () => {
    const selector = headingSelector || Array.from(
      { length: 6 }, ( _, i ) => `main > h` + ( i + 1 )
    )
    const nodes = Array.from( document.querySelectorAll( selector ) )
    const titles = nodes.map( node => ( {
      title: getTitle ? getTitle( node ) : node.innerText,
      depth: getDepth ? getDepth( node ) : Number( node.nodeName[1] ),
    } ) )
    const minDepth = Math.min( ...titles.map( h => h.depth ) )
    const startingOffsets = nodes.map( el => accumulateOffsetTop( el ) - 100 )
    setHeadings( { titles, nodes, minDepth, startingOffsets } )
  }, [headingSelector, getTitle, getDepth] )

  const scrollHandler = throttle( () => {
    const { titles, nodes } = headings
    const offsets = nodes.map( el => accumulateOffsetTop( el ) )
    const activeIndex = offsets.findIndex(
      offset => offset > window.scrollY + 120
    )
    setActive( activeIndex === -1 ? titles.length - 1 : activeIndex - 1 )
  }, throttleTime )
  useEventListener( `scroll`, scrollHandler )
...
}

Struggling with setting the correct types for the parameters passed to accumulateOffsetTop. Any suggestions?

Answer №1

One of the reasons why the error is thrown is due to the fact that el.offsetParent is of type Element | null, not specifically HTMLElement | null. The issue arises because el.offsetParent can potentially return various types of elements, including SVGElement as documented in issue#156. To address this, it is important to utilize type assertions and ensure that the returned value is indeed an HTMLElement if needed.

Answer №2

My final solution involves utilizing type assertions.

const calculateElementOffsetTop = ( element: HTMLElement, cumulativeOffset = 0 ) : number => {
  while ( element ) {
    cumulativeOffset += element.offsetTop - element.scrollTop + element.clientTop
    element = element.offsetParent as HTMLElement
  }
  return cumulativeOffset
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Transformation of Python code into Blockly blocks

As the founder of edublocks.org, I am interested in adding Python to Blocks functionality on the platform. At the moment, users can only transition from Blocks to Python. Is there anyone who has experience with this and can provide guidance on how to achi ...

The Angular framework is unable to locate a differ that supports the object '[object Object]' which is of type 'object'

In my Angular project, I am calling an API and receiving the following JSON data: { "id": 16, "poste": "ameur taboui", "desciption": "f", "service": "f", ...

There is no index signature containing a parameter of type 'string' within the type '{ appointments: { label: string; id: number; minWidth: number; }[]; }'

Just getting started with React and Typescript. I'm attempting to extract data from the configuration file based on the input(props), but it seems like Typescript is throwing errors. Any suggestions on how to tackle this issue? config.json { "t ...

What is the best way to assign ngModel to dynamically inserted input rows in Angular 4+ (specifically in a mat-table)?

Just a quick question - how can I add ngModel to dynamically added new input rows? I have a Mat table with multiple rows and an "add element" method that adds a new row every time a button is clicked. This way, I want to bind the user-entered values and se ...

The error message is indicating that the property `match` is being attempted on an undefined object. This issue is puzzling as it does not reference any specific files or

I encountered an issue while working on my project: I kept receiving the error message "Cannot read property match of undefined." Cannot read property 'match' of undefined The error points to a specific line in polyfills.js: process.version.ma ...

Trouble arises when attempting to import React JSX project/modules from npm into an AngularJS TypeScript module

In the process of developing a proof-of-concept React framework/library, I aim to create a versatile solution that can be utilized in both React and AngularJS applications. To achieve this goal, I have initiated two separate projects: - sample-react-frame ...

Using Angular to access HTML content through the .ts file

Is there a way to retrieve the value of the input field [newUser] when clicking on the button and executing the action [onAddUser()] in the .ts file? <input type="text" ng-model="newUser" style="text-align:center"/> <button (cl ...

Utilizing external imports in webpack (dynamic importing at runtime)

This is a unique thought that crossed my mind today, and after not finding much information on it, I decided to share some unusual cases and how I personally resolved them. If you have a better solution, please feel free to comment, but in the meantime, th ...

What steps should I take to resolve the issue of my endpoint failing to accept POST requests?

I am in the process of developing a customized API, with an endpoint that is specified as shown below: https://i.stack.imgur.com/sZTI8.png To handle the functionality for this endpoint, I have set up a Profiling Controller. Inside my controller directory ...

In order to showcase the data from the second JSON by using the unique identifier

SCENARIO: I currently have two JSON files named contacts and workers: contacts [ { "name": "Jhon Doe", "gender": "Male", "workers": [ "e39f9302-77b3-4c52-a858-adb67651ce86", "38688c41-8fda-41d7-b0f5-c37dce3f5374" ] }, { "name": "Peter ...

Troubleshooting Next.js Route Redirect Failure to Origin URL

I'm currently facing a challenge in my Next.js project where I have a layout component nested inside the app directory. Within this layout component, there's a client-side navbar component that includes a logout button. The goal is to redirect th ...

Get every possible combination of a specified length without any repeated elements

Here is the input I am working with: interface Option{ name:string travelMode:string } const options:Option[] = [ { name:"john", travelMode:"bus" }, { name:"john", travelMode:"car" }, { name:"kevin", travelMode:"bus" ...

Display fresh information that has been fetched via an HTTP request in Angular

Recently, I encountered an issue where data from a nested array in a data response was not displaying properly in my component's view. Despite successfully pushing the data into the object programmatically and confirming that the for loop added the it ...

Show the values in the second dropdown menu according to the selection made in the first dropdown menu using Angular 8

My goal is to retrieve data and populate two dropdowns based on user selection. However, the code I've written isn't giving me the desired output and instead, errors are occurring. Being new to Angular, I would appreciate a review of my code. Her ...

Angular Error: Property 'map' is not found in type 'OperatorFunction'

Code: Using map and switchMap from 'rxjs/operators'. import { map, switchMap } from 'rxjs/operators'; Here is the canActivate code for route guard: canActivate(): Observable<boolean> { return this.auth.userObservable ...

Is it feasible to access and modify local files within an Angular project using TypeScript code in the angular component.ts file? If so, how can this be achieved?

My Angular application is built on version 4 or higher. I have a setup in my project where there is a folder containing a txt file and another folder next to it with an angular component.ts file: FolderWithFile -----file.txt ComponentFolder -----person.co ...

Analysis of cumulative revenue using Palantir Foundry Function

I am in need of creating a function that can transform raw data into target data. The raw data consists of a table with columns for customer, product, and revenue, while the target data includes customer, revenue, and cumulative revenue. customer produ ...

Merging objects with identical keys into a single object within an array using Typescript

Here is the array that I am working with: Arr = [{ code: "code1", id: "14", count: 24}, {code: "code1", id: "14", count: 37}] My objective is to consolidate this into a new array like so: Arr = [{ code: "code1& ...

Comparison between TypeScript's variable scope and JavaScript's variable scope

While researching, I discovered some intriguing discrepancies between the documentation regarding a commonly asked question. The TypeScript docs suggest that variables declared with var will escape the containing function's scope, but according to MS ...

What improvements can I make to enhance my method?

I have a block of code that I'm looking to clean up and streamline for better efficiency. My main goal is to remove the multiple return statements within the method. Any suggestions on how I might refactor this code? Are there any design patterns th ...