Extends the HTMLTableElement and HTMLTableRowElement
MagicJack opened this issue · 10 comments
Hi,
I try to create 2 Custom Elements by extend the HTMLTableElement and HTMLTableRowElement, but it fail.
After further debuging, it failed on the code for prototype chain clone.
defineProperty(o, key, gOPD(p, key));
gOPD(p,key)
return the correct Object, but defineProperty()
fail to define it in new object.
I tested it on IE10, here's part of the js code for extend HTMLTableElement:
var HiTable = Object.create(HTMLTableElement.prototype);
HiTable.setNamePref = function(namePref) {
this.namePref = namePref;
}
document.registerElement('hi-table', { prototype: HiTable } );
...
var myCreateTable = function (captionText, className, panelName) {
var tblObj = document.createElement('hi-table');
tblObj.setNamePref(className+'Row');
if (className)
tblObj.className = className;
if (captionText) {
tblObj.createCaption();
tblObj.caption.innerHTML = captionText;
}
if (panelName)
document.getElementById(panelName).appendChild(tblObj);
return tblObj;
}
...
myCreateTable('Caption Text', 'whatStyle', 'whereToPut');
It fails at createCaption()
in function myCreateTable()
if the prototype
is created from HTMLElement, cause the function belongs to HTMLTableElement only.
It fails at the same point if the prototype
is created from HTMLTableElement, cause:
defineProperty()
fail to define object returned by gOPD(p,key)
(line 110)
Hi, I found the way to solved the problem by change my script.
document.registerElement('hi-table', { prototype: HiTable, extends:'table' } );
...
var tblObj = document.createElement('table', 'hi-table');
any idea why it has to be so?
Hi @MagicJack , first of all thanks for using github instead :-)
So here the thing … there are two ways, as you've realized already, to create Custom Elements
- the one using a tag with at least a dash in the name,
x-bla
,my-table
- the one using the
is
attribute
The main difference between these two methods is that the second one never requires Shadow DOM.
You can think about input
, if you create a my-field
element from HTMLInputElement
prototype but you chose your own my-field
name instead of input
with is
attribute as my-field
, you will not see the input
the way you expect, you'll rather see an empty space.
In order to create same input functionality you need the Shadow DOM behind the scene or you can manually inject the input at runtime or you create an <input>
, and you use is
attribute with your registered class.
I believe same applies for table
element, and generally speaking all elements that are not already recognized by the browser and have a different behavior from HTMLElement
or HTMLUnknownElement
.
However, I've no idea why the code was failing and I'd like to understand if it's a IE only thing or if it's the same in every browser but the short answer to your problem is: use this form for elements with a special behavior
document.registerElement('hi-table', { prototype: HiTable, extends:'table' } );
var tblObj = document.createElement('table', 'hi-table');
document.registerElement('hi-input', { prototype: HiTable, extends:'input' } );
var tblObj = document.createElement('input', 'hi-input');
I hope this help, feel free to close this bug after whenever you want (or ask more)
Hi, @WebReflection:
- I test the code of my 1st post on chrome v36.0.1985.143 m, I got the same error as IE. They both stop at the function
createCaption()
when I create a newht-table
object.
on chrome I got 'Uncaught TypeError: Illegal invocation'
on IE10 it's a simular error message in chinese. - I also try to rewite the workable code in the style you've posted, as below.
But, it does not work! (what happen ?!?)
document.registerElement('hi-table', {
prototype: Object.create(
HTMLTableElement.prototype, {
setNamePref: { value: function() {
this.namePref = namePref;
}},
insertHiRow: { value: function() {
var rowObj = document.createElement('tr', 'hi-row');
var tbObj;
rowObj.init(this.namePref, idx);
if (this.tBodies.length==0) {
this.appendChild(document.createElement('tbody'));
}
tbObj = this.tBodies[0];
while (idx > tbObj.childNodes.length)
tbObj.appendChild(document.createElement('tr'));
if (idx == tbObj.childNodes.length)
tbObj.insertBefore(rowObj);
else
tbObj.insertBefore(rowObj, tbObj.childNodes[idx]);
return rowObj;
}}
}),
extends: 'table'
});
...
var tblObj = document.createElement('table', 'hi-table');
It would help a lot if you could actually write some code I can test … so many thing missing up there … setNamePref
has no parameter, no idea where namePref
comes from. Rest of the logic I don't know what it does, where is hi-row
defined or anything else but for sure nothing happened since last time you told me that other form was working … no commits in here 'cause I don't know what to fix exactly.
The illegal invocation
makes sense to me if you consider what I've said about inherited behavior VS "same kind of DOM node" which is the case of the is
attribute.
I've added a test that use createCaption
without problems. I will close this bug but feel free to ping me back if you have other questions.
Note: nothing actually changed in the code, not sure why your one is failing.
P.S. bear in mind that registration order matters … if you need hi-row
inside hi-table
you should register hi-row
before the table one. Just in case that was your last issue.
OK, Thanks.
I find my bugs. Both code styles can work (thanks for the hint that setNamePref
has no parameter).
also, after some testing, I got 2 conclutions:
1.If I'm just extending HTMLElement
then create an object of what I'm registered (say some other than hi-table
and hi-row
of my code) will work fine.
2. If I want to extend HTMLTableElement
or HTMLTableRowElement
or anything else that's derivation of HTMLElement
, the only possible workable combination is as below
(Still, I don't know why it has to be so.)
var HiTable = Object.create(HTMLTableElement.prototype);
...
document.registerElement('hi-table', { prototype: HiTable, extends:'table' } );
...
var tblObj = document.createElement('table', 'hi-table');
- Have to create the prototype by the derivated one
- Have to register with
extends:
- Have to create the element by orginal with extension
The combination is still true when using the native suport of chrome v36
The codes below are what I used for the testing.
<!DOCTYPE html>
<html>
<head><title>test of registerElement</title>
<script type="text/javascript" src="registerElement-max.js"></script>
<script type="text/javascript">
var HiElement = Object.create(HTMLElement.prototype);
HiElement.func1 = function func1() {
alert("This is function1 Hi-Element");
}
HiElement.func2 = function func2() {
alert("This is function2 Hi-Element");
}
document.registerElement('hi-element', { prototype: HiElement } );
// var HiTable = Object.create(HTMLElement.prototype); // Not workable
var HiTable = Object.create(HTMLTableElement.prototype);
HiTable.func1 = function func1() {
alert("This is function1 of Hi-Table");
}
HiTable.func2 = function func2() {
alert("This is function2 of Hi-Table");
}
document.registerElement('hi-table', { prototype: HiTable, extends:'table' } );
// document.registerElement('hi-table', { prototype: HiTable } ); // not workable
function fnInit() {
var elemObj = document.createElement('hi-element');
elemObj.innerHTML = "Hello World";
elemObj.func1(); // OK
var iPt = document.getElementById('insPt');
iPt.appendChild(elemObj);
// insertAdjacentHTML() belongs HTMLElement
elemObj.insertAdjacentHTML('BeforeBegin', 'BBHeader[');
elemObj.insertAdjacentHTML('AfterEnd', ']');
// var tblObj = document.createElement('hi-table'); // This make createCaption() fail
var tblObj = document.createElement('table', 'hi-table'); // This OK
// This fail if registerElement() without "extends:'table'"
// In chrome v36: tblObj.func1() is undefined
tblObj.func1();
// This fail if createElement() without extendsion
tblObj.createCaption();
tblObj.caption.innerHTML = "The Table Header";
iPt.appendChild(tblObj);
}
window.addEventListener('load', fnInit);
</script>
</head>
<body>
Before The Insert Point
<div id="insPt"></div>
After The Insert Point
</body>
</html>
Thanks for the code and the clarification, my last question would be: is Chrome native producing same results? If yes, we are good .. if not, I might put a big warning in the front page … however, I think the is
attribute js the preferred way in any case for backward compatibility, hopefully not a big deal … or you can still do something like:
var HiTable = Object.create(HTMLTableElement.prototype);
HiTable.createdCallback = function () {
this.table = this.appendChild(
document.createElement('table')
);
};
document.registerElement('hi-table', { prototype: HiTable });
And use the internal table instead
Yes.
Chrome native, Chrome + extension code and IE10 + extension, they produce same results.
Maybe I'll try IE9 and IE11 later.
I think you could add another example for people who want to add extends the derivation of HTMLElement
not just HTMLElement
itself.
added example and explanation.
Best Regards