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

capturing the HTML title and storing it in a JavaScript variable

Is there a way to retrieve the title "Some Name" in the JS file and have it be populated by the hyperlink's title attribute, which is set to "sometitle" in the HTML code? JS var randomtext={ titleText:"Some Name",} HTML <a class="css" title="so ...

Leveraging both onmouseover and onmouseout for container expansion

My goal is to utilize JavaScript along with the HTML events "onmouseover" and "onmouseout" to create a dynamic container. Essentially, I want the container to act as simply a heading when the mouse is not hovering over it, but expand to display additional ...

Is it better to define a function within useEffect or externally?

What is the reason behind defining the fetchData function inside the useEffect instead of outside? Link: https://github.com/zeit/next.js/blob/canary/examples/with-graphql-faunadb/lib/useFetch.js import { useState, useEffect } from 'react' exp ...

Emulating user interaction using Prototype library - Simulate.js

I have set up a Prototype code to act as an observer, but I am facing issues triggering the observer after manually setting the value of the select element... select.observe('change', this.onChange.bindAsEventListener(this)); Initially, I tried ...

Error message encountered when attempting to export functions in Reactjs, resulting in an undefined function error in the context of Reactjs

As I work on creating an adaptive menu using Reactjs and Material UI, I have managed to complete everything. However, I encountered an issue when trying to import a const defined in a different file as the functions are not functioning as expected. The fu ...

Utilize jQuery to transform array values into date objects

I am receiving an array from a .net controller. The values I am getting for dates are: /Date(1445256000000)/ and /Date(1445256900000)/ Instead of this, I need to convert these into proper date values. Now that I have an array of objects, I want to upda ...

Inhibit the ASP:dropdownlist when the value of another ASP:Dropdownlist is selected

Trying to modify two asp.net dropdownlists using client-side JavaScript is my current challenge. Specifically, these dropdowns are located within a modal that opens with a function. The objective is to disable the second dropdown (dropdown2) whenever the ...

Is there a way to execute a JavaScript function on a webpage using Selenium automation?

Here's an element on a website: <span class="log-out-ico" ng-click="logout()"> Instead of clicking it, I want to run the "logout()" script from selenium. Is that possible? If so, how can I do it? This is what I attempted: I ...

Adjust camera focus towards object and utilize the lookAt() method (React three fiber)

Seeking a smooth transition for camera.position and camera.lookAt as I switch between "zoomed out" and "zoomed in" views of individual objects randomly placed. The positioning aspect is working smoothly, but lerping the lookAt() function seems to be causi ...

The battle between ui-sref-active and $state.includes() is a topic of

I am attempting to highlight the clicked area by adding an active class using ui-sref-active. Unfortunately, it is not working with $state.includes(), even though I can see its value as true in the controller. Can someone assist me with this issue? See th ...

Setting key-value pairs in TypeScript objects explained

I encountered an issue with setting key/value pairs on a plain object. type getAObjectFn = <K extends string, V>(k: K, v: V) => Record<K, V> const getAObject: getAObjectFn = (k, v) => { return { [k]: v } } console.log(getAObject ...

Modifying tags or categories of a post using a sidebar plugin in WordPress Gutenberg

I am currently working on integrating the functionality to add or delete a post's tag or category using a WordPress Gutenberg sidebar plugin built with React and JavaScript. Despite the lack of detailed resources on how to implement this particular us ...

What are the steps for leveraging a proxy with NodeJS and Selenium?

While researching the topic on proxies in the documentation, I came across a method to set up a proxy while building a driver like this: var driver = new webdriver.Builder() .withCapabilities(webdriver.Capabilities.chrome()) .setProxy(proxy.manual ...

How can I adjust the gravity or positioning of a tipsy tooltip in jQuery?

Is there a way to adjust the position or gravity of Tipsy? The plugin offers various options for gravity that can be set through the script: nw | n | ne | w | e | sw | s | se Currently, I have set it to s position, as shown in this demo: http://jsfiddle. ...

What steps need to be taken to implement a structured autocomplete feature?

Let me break down the workflow for you: The user inputs something in a text field. Upon keypress, the frontend communicates with our backend script to retrieve and select a specific value. Once the value is selected, on leaving the input field, we query ...

The property 'caseSensitive' is undefined and cannot be read

After creating a simple code, I am puzzled by an error message that seems to be case sensitive. However, the code appears correct based on my understanding. Here is the code snippet: App.js const express = require('express'); const path = requir ...

Vue JS i18next: Handling Single Translation String Fallbacks

Recently, I've been utilizing i18next, and I decided to set a fallback value for some translated strings in case they are not available in the selected language. Here's an example: en: base.json "yes": "yes" "no": "no" fr: base.json ...

Utilizing the getJSON Method to Automatically Fill a Dropdown Selection Element

I am looking to populate a dropdown select menu with bank names and IIN numbers obtained from the following JSON: JSON Data : {"status":true,"message":"Request Completed","data":[{"id":1,"activeFlag":1,"bankName":"Union Bank of India","details":"Union Ba ...

In JavaScript, the code is designed to recognize and return one specific file type for a group of files that have similar formats (such as 'mp4' or 'm4v' being recognized as 'MOV')

I am working on a populateTable function where I want to merge different file types read from a JSON file into one display type. Specifically, I am trying to combine mp4 and m4v files into MOV format. However, despite not encountering any errors in my code ...

Tips for managing numerous Axios requests in JavaScript

I have a scenario where I need to send 3 axios calls simultaneously from the frontend to the backend using the axios.all function to modify a MONGO DB database. The challenge I am facing is ensuring that if one of the requests fails, none of the other requ ...