DEV Community

Cover image for Re-implementing jQuery methods in the HTMLElement prototype

Re-implementing jQuery methods in the HTMLElement prototype

Jochem Stoel on December 06, 2018

It is almost 2019 and people have generally come to the agreement that jQuery is deprecated. I for one have a very different perspective on that bu...
Collapse
 
nektro profile image
Meghan (she/her) • Edited

Some notes:

  • $$('p', $('article')) returns all <p> elements in the first <article>, not all <p>s that are children of <article>s. For that you'd still have to do $$("article > p")
  • HTMLElement.prototype.has = HTMLElement.prototype.hasAttribute doesn't bind the function properly, so this will not work properly
  • your .text() function should use .textContent not .innerText
  • HTMLElement.append should still check to see if child is an instance of Node, even if it's not an HTMLElement
  • Same with HTMLElement.prototype.prepend
  • Also HTMLElement.prototype.prepend, already exists
  • HTMLElement.prototype.remove already exists too
  • HTMLElement.prototype.parent should return .parentElement
  • HTMLElement.prototype.emit should have args=null as a parameter and use new CustomEvent(event, { detail:args })
Collapse
 
jonaskuske profile image
Jonas

HTMLElement.prototype.append and NodeList.prototype.forEach already exist, too.

Collapse
 
jochemstoel profile image
Jochem Stoel

Thanks I fixed the things you said in updated gist. Why does text need to return textContent and not innerText?

Collapse
 
nektro profile image
Meghan (she/her) • Edited

innerText is a lot more performance heavy and triggers layout to be recalculated.
stackoverflow.com/a/35213639/5203655

Thread Thread
 
daniel15 profile image
Daniel Lo Nigro

AFAIK, innerText is also non-standard (originally IE-only) whereas textContent is part of the standard.

Collapse
 
krofdrakula profile image
Klemen Slavič

Nice write-up of extending the base prototypes!

I do have a bit of a hang-up with modifying prototypes of builtins without using Object.defineProperty, though. If any application is inspecting the prototype, those properties are enumerated, so it might be better to define them as non-enumerable on the prototype object, just to be on the safe side. A factory function that would augment a built in by taking a property name and a function would make the examples just as readable. :)

I love articles like this that make it clear just how thin an abstraction layer you can have on top of the standard web API.

Collapse
 
somedood profile image
Basti Ortiz

It also gives you an idea on what Babel does under the hood.

Collapse
 
qm3ster profile image
Mihail Malo • Edited

A word of warning:
ParentNode.append() and ParentNode.prepend(), which have bazonkers browser support (everything but IE), behave like Node.appendChild()(But can take strings, and multiple nodes) and not like $().append().
The sibling addition behavior, like the $().append() is implemented as ChildNode.after() and ChildNode.before(). It has the same affordances as the ParentNode.append() but is less supported (No Safari, no mobile Edge)

Collapse
 
fernandosavio profile image
Fernando Sávio • Edited

I think you should test if string is undefined in html() and text(), otherwise it wouldn't be possible to set it empty.

PS: I am commenting from my phone, so forgive the lack of details :)

Collapse
 
jochemstoel profile image
Jochem Stoel

If HTML and/or TEXT is empty you want to return the innerHTML and innerText so no.

Collapse
 
fernandosavio profile image
Fernando Sávio

How would you simulate jQuery $('#el').html(''); ?

Thread Thread
 
jochemstoel profile image
Jochem Stoel

You can literally use my example already provided in the post:

window.$ = (query, ctx = document) => ctx.querySelector(query)
window.$$ = (query, ctx = document) => ctx.querySelectorAll(query)

HTMLElement.prototype.html = function (string) {
    if (!string)
        return this.innerHTML
    this.innerHTML = string
    return this
}

then

$('#el').html('')
Thread Thread
 
fernandosavio profile image
Fernando Sávio • Edited

That wouldn't work because '' would evalute to false and in your code it would be the same as $('#el').html().

Check it running: codepen.io/fernandosavio/pen/qLWeWV

Thread Thread
 
jochemstoel profile image
Jochem Stoel

Well whatta ya know. You are right.
Change the line

if (!string)

into this

if (typeof string == 'undefined')

so that you get

window.$ = (query, ctx = document) => ctx.querySelector(query)
window.$$ = (query, ctx = document) => ctx.querySelectorAll(query)

HTMLElement.prototype.html = function (string) {
    if (typeof string == 'undefined')
        return this.innerHTML
    this.innerHTML = string
    return this
}

$('#test').html('');

codepen.io/jochemstoel/pen/OrJLbR

You can also replace it with

if(typeof string != 'string')

or even

if(string == undefined)
Collapse
 
daniel15 profile image
Daniel Lo Nigro

Not sure if you've ever used it, but this is basically the approach Prototype.js took (and MooTools copied). The issue back in the Prototype.js era was that extending the native prototypes didn't work in IE6, so it had to also support a different approach (a wrapper object) in IE.

Collapse
 
lorenz1989 profile image
lorenz1989

Below is the suggestion from ChatGPT that I requested it.

ChatGPT

Collapse
 
gergelypolonkai profile image
Gergely Polonkai

I used jQuery exclusively for $.ajax() (yeah, i know. But i’m not a front-end dev) until i finally took some time and learn about XMLHttpRequest. And now there’s this Fetch thing…

Collapse
 
ajnasz profile image
Lajos Koszti

elmenet.attr('min', 0) will return the attribute value.

better check arguments.length

Collapse
 
nikoheikkila profile image
Niko Heikkilä

These are very nice! Have you also tried implementing Ajax calls and stuff without jQuery? For a long time those were a big reason for sticking with jQuery but then Fetch API came.

Collapse
 
jochemstoel profile image
Jochem Stoel

No I did not, for the reason you mentioned. We have fetch nowadays. Would you like me to, though?

Collapse
 
caseycole589 profile image
Casey Cole

Yes that would be a cool post

Collapse
 
akosipau profile image
akosipau

How can it be implemented in angular?