在 HTMLElement 原型中重新实现 jQuery 方法

2025-06-07

在 HTMLElement 原型中重新实现 jQuery 方法

快到2019年了,大家普遍认为jQuery已被弃用。我个人对此持有不同看法,但那是以后的事了。那么,为什么人们还在使用它呢?是旧习惯?还是方便?事实证明,大多数使用jQuery的人只在少数几个方法上使用它。

我认为对于新手程序员来说,将这些 jQuery 方法重新实现到 HTMLElement 原型上会很有趣且很有教育意义。

查询选择器

首先,我们来为文档查询选择器 (querySelectors) 定义一个快捷方式。$表示单个元素,$$表示所有匹配项。我们希望能够像 jQuery 那样提供第二个上下文参数,默认为整个文档。我假设使用的是 ES6+ 版本,其中函数声明中的默认值是支持的。

/**
 * $ for document.querySelector
 * $$ for document.querySelectorall
 * with optional context just like jQuery (defaults to document)
 */
window.$ = (query, ctx = document) => ctx.querySelector(query)
window.$$ = (query, ctx = document) => ctx.querySelectorAll(query)
Enter fullscreen mode Exit fullscreen mode
$('h2') // will return single _<h2>_ element
$$('h2') // will return array with all _<h2>_ elements
Enter fullscreen mode Exit fullscreen mode

使用上下文参数,我们可以选择另一个元素 (<article></article>) 中的所有 <p> 元素

$$('p', $('article'))
Enter fullscreen mode Exit fullscreen mode

NodeList 迭代

不得不承认,jQuery.prototype.each也相当简洁。在 NodeList 原型上添加一个each属性,并将值赋给一个函数,该函数将NodeList转换为数组,然后使用提供的回调函数对其进行迭代。

/**
 * This allows you to "forEach" a NodeList returned by querySelectorAll or $$
 * similar to jQuery.prototype.each
 * use: $$('li').each(callback)
 */
Object.defineProperty(NodeList.prototype, 'each', {
    value: function (fn) {
        return Array.from(this).forEach((node, index) => fn(node, index))
    }
})
Enter fullscreen mode Exit fullscreen mode

属性

另一个常见的 jQuery 方法是attr。我们可以使用attr 函数读取和写入 DOM 元素的属性,只需一个方法即可。我们将为该方法添加一个小功能:当未提供任何参数时,它将返回所有属性。attr还附带
removeAttr函数用于移除属性),并且需要判断是否存在参数。

/** 
 * single method to get/set/list attributes of HTMLElement. 
 * get argument id:     $('div').attr('id')
 * set argument id:     $('div').attr('id', 'post123')
 * list all arguments:  $('div').attr()  // Fuck yeah
 */
HTMLElement.prototype.attr = function (key, value) {
    if (!value) {
        if (!key) {
            return this.attributes
        }
        return this.getAttribute(key)
    }
    this.setAttribute(key, value)
    return this
}

/**
 * remove attribute from HTMLElement by key
 */
HTMLElement.prototype.removeAttr = function (key) {
    this.removeAttribute(key)
    return this
}

/**
 * check whether a DOM node has a certain attribute.
 */
HTMLElement.prototype.has = function(attribute) {
    return this.hasAttribute(attribute)
}
Enter fullscreen mode Exit fullscreen mode

innerText 和 innerHTML

/** 
 * single function to get and set innerHTML
 * get:  $('body').html()
 * set:  $('body').html('<h1>hi!</h1>')
 */
HTMLElement.prototype.html = function (string) {
    if (!string)
        return this.innerHTML
    this.innerHTML = string
    return this
}

/** 
 * single function to get and set innerText
 * get:  $('body').text()
 * set:  $('body').text('hi!')
 */
HTMLElement.prototype.text = function (string) {
    if (!string)
        return this.textContent
    this.innerText = string
    return this
}
Enter fullscreen mode Exit fullscreen mode

附加和前置

以下append方法允许你在指定的目标元素末尾插入一个 HTML 元素。prepend方法会将其插入到目标元素之前。

/**
 * append HTMLElement to another HTMLElement
 * like jQuery append()
 */
HTMLElement.prototype.append = function (child) {
    if (child instanceof HTMLElement) {
        this.appendChild(child)
        return this
    }
    this.append(child)
    return this
}

/**
 * prepend HTMLElement to another HTMLElement
 * like jQuery prepend()
 */
HTMLElement.prototype.prepend = function (sibling) {
    if (sibling instanceof HTMLElement) {
        this.parentNode.insertBefore(sibling, this)
        return this
    }
    this.parentNode.insertBefore(sibling, this)
    return this
}
Enter fullscreen mode Exit fullscreen mode

删除元素

在 JavaScript 中,删除元素是通过访问其父节点并调用 removeChild() 来实现的。我知道这有点奇怪。

HTMLElement.prototype.remove = function() {
    this.parentNode.removeChild(this)
}
Enter fullscreen mode Exit fullscreen mode

你可能知道, jQuery 中不能使用箭头函数。但是

$('#foo').remove()
// or
$$('div').each(element => element.remove()) 
Enter fullscreen mode Exit fullscreen mode

父母

获取节点的父节点。

/** 
 * get a HTMLElement's parent node
 * use: $('h1').parent()
 */
HTMLElement.prototype.parent = function () {
    return this.parentNode
}
Enter fullscreen mode Exit fullscreen mode

活动

现代 JavaScript 库实现onoffemit来获取、设置和分派事件。

/**
 * add event listener to HTMLElement
 * $(document).on('click', event => ...)
 */
HTMLElement.prototype.on = function (event, callback, options) {
    this.addEventListener(event, callback, options)
    return this
}

/**
 * remove event listener from HTMLElement
 * $(document).off('click', callback)
 */
HTMLElement.prototype.off = function (event, callback, options) {
    this.removeEventListener(event, callback, options)
    return this
}

/**
 * dispatch an event on HTMLElement without needing to instanciate an Event object.
 * $(document).emit('change', { foo: 'bar' })
 */
HTMLElement.prototype.emit = function (event, args = null) {
    this.dispatchEvent(event, new CustomEvent(event, {detail: args}))
    return this
}
Enter fullscreen mode Exit fullscreen mode

数据集

最后但同样重要的是,这是一种访问数据属性的好方法。

/**
 * single method to get/set/list HTMLElement dataset values
 * get:  $('div').data('color')     assuming <div data-color="..."></div>
 * set:  $('div').data('color', '#0099ff')
 */
HTMLElement.prototype.data = function (key, value) {
    if (!value) {
        if (!key) {
            return this.dataset
        }
        return this.dataset[key]
    }
    this.dataset[key] = value
    return this
}
Enter fullscreen mode Exit fullscreen mode

定义

这与 jQuery 无关,但仍然是一个很好的捷径。

/**
 * Convenient shortcut 
 * use:   define('property', { ...descriptor })
 */
Object.defineProperty(window, 'define', {
    value: (property, ...meta) => meta.length == 2 ? Object.defineProperty(meta[0], property, meta[1]) : Object.defineProperty(window, property, meta[0]),
    writable: false,
    enumerable: true
})
Enter fullscreen mode Exit fullscreen mode

现在我们可以这样做:

/** 
 * now | single statement accessor that returns current time
 * @returns {number} 
 */
define('now', {
    get: Date.now
})
Enter fullscreen mode Exit fullscreen mode

标识符now将返回当前时间。您无需将其作为函数调用,只需访问它即可。

setInterval(() => console.log(now), 10)
/*
1543930325785
1543930325795
1543930325805
1543930325815
1543930325825
1543930325835
*/
Enter fullscreen mode Exit fullscreen mode

要旨

为了您的方便,以上所有内容均已概括。
https://gist.github.com/jochemstoel/856d5b2735c53559372eb7b32c44e9a6

文章来源:https://dev.to/jochemstoel/re-implementing-jquery-methods-in-the-htmlelement-prototype-15b9
PREV
编程音乐:Spotify 编程播放列表
NEXT
🤓 🚀 这 6 个 repos 将帮助你征服 React 服务器组件 (RSC) ✅🔥