我想写一些扩展DOM节点的Javascript类(这样我就可以直接将我的类的实例插入到DOM中),但是我很难找到我应该继承的类/原型。
E.g:
function myExtendedElement() {
this.superclass = ClassA;
this.superclass();
delete this.superclass;
}
但ClassA应该是什么?
答案 0 :(得分:19)
执行此操作不是一个好主意。
首先,要从DOM元素继承,您需要访问该元素的原型。问题是并非所有浏览器都提供对DOM元素原型的访问。例如,较新的Gecko和基于WebKit的客户端将这些原型中的一些暴露为全局对象--HTMLDivElement,HTMLElement,Element,Node等。
例如,普通的DIV元素通常具有类似于:
的原型链HTMLDivElement.prototype -> HTMLElement.prototype -> Element.prototype
-> Node.prototype -> Object.prototype -> null
您可以访问其中任何一个并根据需要扩展或继承。但同样,即使你可以,我强烈建议不要。
当浏览器没有公开这些原型时,你几乎运气不好。您可以尝试按照DOM元素本身的constructor
属性检索它们 -
document.createElement('div').constructor;
- 但是不能保证元素具有constructor
属性(例如IE6没有),即使它确实存在,该属性也引用“正确”对象。毕竟,如果构造函数确实引用了正确的对象,那么仍然无法保证允许对此对象进行扩充。事实上,允许宿主对象实现完全奇怪的行为,甚至不必遵循原生JS对象遵循的规则(你可以在现实生活中找到几十个这样的例子)。
您希望避免从DOM元素原型继承的第二个原因是这种继承的机制在任何地方都没有真正指定;它可能是古怪的,不可预测的,整体脆弱和不可靠。
是的,你可以创建一个构造函数来初始化具有适当原型链的对象(即在其中包含DOM原型):
function MyDivElement(){}
MyDivElement.prototype = HTMLDivElement.prototype;
var myDiv = new MyDivElement();
typeof myDiv.appendChild; // "function"
- 但这和它一样多,并且通过在原型中使用某些方法而不是其他任何方法来限制整个方法的有用性 -
typeof myDivElement.nodeName; // "undefined"
myDivElement.innerHTML = '<span>foo<\/span>';
myDivElement.childNodes; // Error
在某些标准指定从DOM原型继承的确切机制(以及浏览器实际实现该机制)之前,最好不要将它们单独使用,并且可能尝试替代方法 - 例如包装器或装饰器模式而不是原型:)
答案 1 :(得分:2)
但是,如果您在很多人/作者之间共享代码或处理DOM不确定性,通常最好使用新的工厂方法创建适配器/包装器对象以在继承方案中使用。
在这种情况下,我编写了document.createExtEl来创建包装的DOM元素,其可访问的属性都可以通过原型获得。
使用以下内容,你的div的“超类”将是HTMLExtDivElement(在这种情况下全局可用 - ew,但它只是一个例子)。对原始HTMLElement实例的可用属性的所有引用都存在于包装器的原型中。注意:一些旧的IE属性不能作为引用传递,甚至不能抛出错误(真棒),这就是try / catch的用途。
您可以通过在循环包装实例可用属性之后添加逻辑来放置缺少的或标准化的属性来规范化公共属性,但我会留给您。
现在为了皮特的爱,不要用我的代码写一些级联的16次继承愚蠢,然后在一些具有讽刺意味的流行图书馆实施,我们都被迫处理或者我将会大声追捕你引用“设计模式”,同时扔掉腐烂的水果。
//Implementation just like document.createElement()
//document.createExtEl('div').tagName === 'DIV'
document.createExtEl = ( function(){ //returns a function below
var htmlTags = ['div','a'], //... add all the element tags you care to make extendable here
constructorMap = {},
i = htmlTags.length;
while(i--){
thisTag = htmlTags[i].toLowerCase();
constructorMap[ thisTag ] = function(){
var elObj = document.createElement(thisTag),
thisProto = this.constructor.prototype,
constructorName = 'HTMLExt' + thisTag.charAt(0).toUpperCase() + thisTag.slice(1) + 'Element';
alert(constructorName);
window[constructorName] = this.constructor; //adds a global reference you can access the new constructor from.
for(var x in elObj){ try{ thisProto[x] = elObj[x]; } catch(e){} }
}
}
//all of the above executes once and returned function accesses via closure
return function(tagName){
return new constructorMap[tagName.toLowerCase()]();
}
} )()
//Now in the case of a superclass/constructor for div, you could use HTMLExtDivElement globally
答案 2 :(得分:1)
您可以简单地向DOM原型添加新功能,例如
Element.prototype.myNameSpaceSomeFunction = function(...){...}
然后myNameSpaceSomeFunction
将存在于所有元素上。
答案 3 :(得分:1)
在2020年,您可以通过扩展HTML元素轻松创建自定义元素。
class AppDrawer extends HTMLElement {...}
window.customElements.define('app-drawer', AppDrawer);
// Or use an anonymous class if you don't want a named constructor in current scope.
window.customElements.define('app-drawer', class extends HTMLElement {...});
更多信息和参考:Custom Elements
答案 4 :(得分:0)
我发现了一个有效的hack ...至少,它使我能够通过DOM元素访问扩展对象属性,反之亦然。但它并不优雅。
var DOMelement = document.getElementById('myID'); // or $('#myID')[0]; in jQuery
DOMelement.extended = new extensionClass(this);
function extensionClass(element) {
this.element = element;
...
}