如何扩展DOM元素作为一个类(不使用jQuery)

5

可能重复:
在JavaScript中,您可以扩展DOM吗?

我正在尝试向Element添加方法和属性。它没有列为全局类。我可以像这样扩展String:

String.prototype.dosomething = function { ... };

我尝试像这样做(它更大,这是基本的):
function $id(str){
    this.element = document.getElementById(str); 
    return element;
}
var myElem = $id('myId'); // WORKS !!!
$id.prototype.dosomethig = function ( ... }; 
var myValue = $id('myId').dosomething(); // DOESN'T WORK !!!

我尝试了其他方法,但问题总是相同的,当我尝试扩展类时,它不起作用。有没有一种方法可以避免这种情况?我知道jQuery可以做到这一点,但我想做自己的 - 如果他们能做到,我也可以……对吗?

编辑: 以下是在Safari和Firefox中已经工作的代码……

Element.prototype.pos = function(){
    var curleft = this.offsetLeft;
    var curtop = this.offsetTop;
    var scrleft = this.scrollLeft;
    var scrtop = this.scrollTop;
    var pElement = pElementScr = this.offsetParent
    while(pElement){
        curleft += pElement.offsetLeft;
        curtop += pElement.offsetTop;
        pElement = pElement.offsetParent;
    }
    while(pElementScr){
        scrleft += pElementScr.scrollLeft;
        scrtop += pElementScr.scrollTop;
        pElementScr = pElementScr.offsetParent;
    }
    return {x:this.offsetLeft, y:this.offsetTop};
}

它可以确定一个div在很多其他div内部的位置。在解决了我的库中的许多其他问题后,我将尝试在IE8中进行测试。


2
你应该阅读http://perfectionkills.com/whats-wrong-with-extending-the-dom/。 - Felix Kling
1
据我所知,jQuery不会这样做。它们提供了一个包装器,但避免触及DOM原型。 - tucuxi
4个回答

4

在Firefox、Chrome和IE8+中,元素继承自Element.prototype,因此您必须执行以下操作:

Element.prototype.doSomething = function() {
    alert("hello");
};

扩展主机原型非常脆弱且不建议使用,但如果您只是玩玩应该没问题。其主要原因是DOM规范没有指定原型实现而仅仅提供接口, elem.appendChild() 应该可以正常工作,它不必存在于某些原型中。

这段代码:

function $id(str) {
    this.element = document.getElementById(str);
    return element;
}

这段代码之所以能够工作,是因为在没有任何对象上下文的情况下调用函数时,this引用全局对象(虽然它应该在看到this时直接抛出错误,但是严格模式通过将其设置为undefined来修复了这个问题)。你正在创建一个全局变量element(因为对全局对象的属性赋值会变成全局变量),然后返回该全局变量。


请您详细说明,为什么它很脆弱? - srini.venigalla
@srini.venigalla 请查看FelixKling对您的问题的评论。 - Esailija
同意 - 这非常脆弱,应该避免。 - tucuxi
@Esailija - 好的,这与 DOM 有关,我同意,以为你指的是一般原型扩展很脆弱。 - srini.venigalla
我正在编写一个小型库,其中我正在识别浏览器(类型和版本),以便我能够为每种情况编写代码。Element.prototype在我的Safari上运行良好,是一个不错的起点! - Gustavo

2

为什么不像jQuery一样,对DOM对象进行包装而不是扩展它。

var myLib = {
   addWrapper: function ( id ) {
      return new this.WrapperClass( document.getElementById( id ) );
   },
   WrapperClass: function ( element) {
      this.element = element;
   }
};
MyLib.WrapperClass.prototype.someMethod = function() {
   alert( this.element);
}

var wrappedElement = myLib.addWrapper( 'someid' );
wrappedElement.someMethod();

我从不那样工作,我会尝试一下。 - Gustavo

0
$id('myId').dosomething()

$id('myId') 是一个元素。

你在 'Element' 中没有名为 doSomething 的函数,而是在 $id 上有它,$id 是一个函数。实际上,当你将一个函数添加到另一个函数时,会发生什么呢?这可能吗?


他并没有将一个函数分配给另一个函数,而是将其分配给$id.prototype.dosomething,而$id.prototype是一个对象。但当然,由于函数也是对象,它们可以具有任意属性。 - Felix Kling
@Felix 对的.. $id 返回一个元素,他给它添加了一个函数,然后为'myId'创建了一个实例,为什么它没有得到扩展呢? - srini.venigalla
他返回了element,实际上应该抛出一个错误,因为它没有被定义。他必须从$id中返回this - Felix Kling
JavaScript对于很多人来说并不清晰。创建一个类和创建一个函数的方式是一样的,只是视角的问题。 - Gustavo

0

首先,扩展DOM通常(读作:总是)是一个不好的选择。如果你想编写快速、跨浏览器的代码,就不应该扩展DOM或其他本地对象(Object、String等)。扩展本地对象还可能导致与库等的兼容性问题。如果你想了解更多关于这个主题的内容,我建议你看一下这篇优秀的博客文章:http://perfectionkills.com/whats-wrong-with-extending-the-dom/

如果支持旧版本的IE(IE6和7)对你不重要,那么这就是你想要的:

Element.prototype.doSomething = function () {
    // Your code here
};

在你的示例中,问题在于 $id 返回一个元素。你的 "doSomething" 是附加到函数 $id 而不是它返回的元素。
按照以下方式操作即可解决此问题:
function $id(str){
    this.element = document.getElementById(str); 
    return this;
}

很好。如果能在IE8中运行,这几乎可以解决所有问题。我不打算与旧版本兼容。有趣的是,我的所有js引用列表中都没有将Element列为类。 - Gustavo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接