Creating a unique custom selector with TypeScript that supports both Nodelist and Element types

I am looking to create a custom find selector instead of relying on standard javascript querySelector tags. To achieve this, I have extended the Element type with my own function called addClass(). However, I encountered an issue where querySelectorAll returns a NodeListof Element while querySelector returns just a single Element. This inconsistency led TypeScript to flag an error since the result of find could be either a NodeListof Element or just a single Element. My goal is to be able to utilize my find function for both single and multiple element selections, and also apply my custom functions like addClass or forEach (which are NodeList methods).

function find(selector: string) 
    {
       let elements = document.body.querySelectorAll(selector);
       if (elements.length === 1) return elements[0] as Element;
       else return elements;
    }

    interface Element 
    {
        addClass(className: string): Element;
    }

   Element.prototype.addClass = function (className) 
    {
       this.classList.add(className);
       return this;
    }

In order to address this issue, I attempted to add a forEach method to the Element interface and extend the NodeList interface with my custom Element interface. However, I suspect that this may not adhere to best practices in TypeScript. Despite this, I was able to successfully implement forEach for a single Element and addClass method for a NodeList Element.

 interface Element 
        {
            addClass(className: string): Element;
            forEach(callback: Function): any;
        }
    
    interface NodeList extends Element {}
    
    Element.prototype.forEach = function (myFunction: Function) 
    {
      myFunction(this);
    }

Additionally, I introduced a find method within my Element interface

interface Element 
    {
        addClass(className: string): Element;
        forEach(callback: Function): any;
        find(selector: string): NodeList | Element;
    }

Element.prototype.find = function (selector) 
{
  let elements = document.body.querySelectorAll(selector);
  if (elements.length === 1) return elements[0] as Element;
  else return elements;
}

While this solution works for now, I am curious if there is a more efficient approach to resolving this problem in TypeScript?

Answer №1

util.tsTest Playground Link

type ElementExtended = Element & {
  classAdd: typeof classAdd,
}

function classAdd(this: Element, ...tokens: string[]) {
  this.classList.add(...tokens)
}

function query(selector: string): NodeListOf<ElementExtended> {
  return document.querySelectorAll(selector)
}

function queryArray(selector: string): ElementExtended[] {
  return Array.from(query(selector))
}

(Element.prototype as ElementExtended).classAdd = classAdd

export {
  query,
  queryArray,
}

test.js

import { query } from './util'

query('foo')[0]?.classAdd('bar')

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

Update Button Visibility Based on State Change in ReactJS

Currently, I'm utilizing a Material UI button within my React application. Despite changing the state, the button remains visible on the screen. Here's the relevant code snippet: function MainPage() { const state = useSelector(state => sta ...

Presentation for a div's background image with the use of jQuery

My header contains a large div with text contents and boxes, along with a background image. Now I want to create a slideshow for this div's background. Does anyone know how to make a slideshow for a div's background image? I've searched ex ...

Error during Next.js build: Incompatible types - cannot assign type to 'never'

Encountering an error in the console while attempting to build my project: .next/types/app/facebook/page.ts:8:13 Type error: Type 'OmitWithTag<typeof import("D:/projects/abkh24/client/app/facebook/page"), "metadata" | "defa ...

The socket.io-client could not be located on the running Node.js server

Setting up a fresh installation of Node, Express, and Socket.io on a Linux environment using npm. When attempting to run a sample from the official socket.io source, I encountered an error stating that the 'socket.io-client' module was not found ...

Calculating the hour difference between two time stamps (HH:MM:SS a) using moment.js

I have two time without date var startTime="12:16:59 am"; var endTime="06:12:07 pm"; I need to calculate the total hours between the above times using a library like moment.js. If it's not achievable with moment.js, then please provide a solution u ...

The ExtJS Grid Filter is being triggered excessively

I am working on an ExtJS 6.2 grid that uses the 'classic' API. Although I am not very experienced with Ext, we have a grid component that we reuse with small modifications in different applications. In one of our apps, we have a text field for fi ...

What could be causing the issue with React Router not functioning properly in Material UI?

I'm currently working on creating a tab and adding routing inside the first tab. However, I am facing an issue where the desired page does not display after clicking on the link. Strangely, upon refreshing the page, the corresponding page appears as e ...

Setting up React Context API with TypeScript: A Step-by-Step Guide

I recently updated my app.js file to app.tsx for improved functionality. However, I encountered an error with the current value. app.tsx import React, { createContext, useState } from "react"; import SelectPage from "./pages/select/select& ...

Retrieving data from a remote source repeatedly based on user selections on a single page

In my current project, I am utilizing next js to build a web application. One of the pages in this app has the following structure: <page> <component_1> This component fetches data from a remote server using `getInitialProps` (alternat ...

Altering the "src" property of the <script> tag

Can the "src" attribute of a current <script> element be altered using Jquery.attr()? I believed it would be an easy method to enable JSONP functionality, however, I am encountering difficulties in making it operate effectively. ...

Mastering hot reloading in React when using Dotnet and DockerorAchieving seamless hot reloading in

I have been working on getting hot reloading to function properly with React, Docker, and Dotnet. However, my research has shown that only static rendering is compatible with Docker. As a result, I find myself having to run the command docker -t build {Na ...

Update the knockout values prior to submitting them to the server

Imagine having a ViewModel structured like this with prices ranging from 1 to 100... var Item = { price1: ko.observable(), price2: ko.observable(), price3: ko.observable(), ... ... price100: ko.observable(), name: ko.observable ...

Invoke the componentDidMount() method in a React Component that is not a subclass of React.Component

I have a react component that I render later in my index.js file index.js import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; ReactDOM.render( <React.StrictMode> <App /> ...

Configure Protractor's configuration file to utilize a personalized reporter

I'm in the process of setting up end-to-end tests using protractor.js, but I am not happy with how the default reporter displays results on my terminal window. Is there a way to customize the reporter configuration to make it easier to read and more ...

Discover updates within a JQuery Ajax call

I am sorry if this question sounds simple, but I would like to know how to set up a function that triggers when the Ajax data changes from the previous request. window.setInterval(function(){ $.get("feed", function(data){ if (data.changed ...

NPM is searching for the package.json file within the user's directory

After completing my test suite, I encountered warnings when adding the test file to the npm scripts in the local package.json. The issue was that the package.json could not be located in the user directory. npm ERR! path C:\Users\chris\pack ...

Executing asynchronous operations and handling responses in Node.js with Express

While I am still getting the hang of asynchronous functions and callbacks in Node.js, my current struggle lies in figuring out how to return a response after reading data from a file during an asynchronous operation. I have managed to send a response usin ...

Using Ajax to preview images results in displaying a broken image icon

I am currently working on implementing an image preview function using Ajax. As I was experimenting, a couple of questions came to my mind: Once the Ajax has been executed, is the actual image uploaded to the server or just an array containing strings l ...

Concealing a section of a table with JavaScript while preserving the original table structure

I made some adjustments to the script tag: $(document).ready(function) { Now, the evenTd's are hidden correctly. Here is the table with the code provided: <table> <tr> <td>itemOne</td> <td class="even ...

Issue: Unable to locate file 'socket.io-client/dist/socket.io.min.js'

Attempting to set up the Spika backend (node application), I ran into an issue while trying to start the server in standalone mode with the command: $ node src/server/main.js The error that popped up was: Error: Cannot find module 'socket.io-clien ...