A user account is required in order to edit this wiki, but we've had to disable public user registrations due to spam.

To request an account, ask an autoconfirmed user on Chat (such as one of these permanent autoconfirmed members).

Component Model Strawman: Element Registration: Difference between revisions

From WHATWG Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
 
(29 intermediate revisions by the same user not shown)
Line 1: Line 1:
= Overview =
= Overview =


With the Element Registration. page authors can tell new element names and its behavior to agents.
With the Element Registration mechanism.
page authors can tell new element names and its behavior to agents.


= The <tt>element</tt> element and <tt>HTMLRegistrationElement</tt> =
= The <tt>element</tt> element and <tt>HTMLRegistrationElement</tt> =
Line 18: Line 19:
document.head.appendChild(element);
document.head.appendChild(element);
// Shorter form.
// Shorter form.
HTMLRegistrationElement.register("x-comment");
document.tags.register("x-comment");


<!-- Markup API -->
<!-- Markup API -->
<head>
<head>
   <element for="x-comment"></element>
   <element name="x-comment"></element>
</head>
</head>


Line 32: Line 33:


The <tt>element</tt> element allows <tt>script</tt> elements in its children.
The <tt>element</tt> element allows <tt>script</tt> elements in its children.
These script blocks are invoked as a function, whose receiver(<tt>this</tt>) is bound to
These script blocks are executed over a global object.
Before the evaluation, <tt>HTMLRegistrar.registering</tt> is set to the
enclosing <tt>element</tt> element. These script blocks can be used for providing  
enclosing <tt>element</tt> element. These script blocks can be used for providing  
lifecycle callback definitions for the registering element.
lifecycle callback definitions for the registering element.
Line 39: Line 41:


<head>
<head>
   <element for="x-comment">
   <element name="x-comment">
     <script>
     <script>
     var shouldBeXComment = this.name;
     var shouldBeXComment = document.tags.registering.name;
     <script>
     <script>
   </element>
   </element>
Line 48: Line 50:
</pre>
</pre>


=== The <tt>create</tt> callback ===
=== The <tt>creator</tt> callback ===


The <tt>HTMLRegistrationElement</tt> provide a pair of lifecycle callbacks
The <tt>HTMLRegistrationElement</tt> provide a pair of lifecycle callbacks
for the element instance creation including the tree construction.
for the element instance creation including the tree construction.


The first callback, <tt>create</tt> is invoked when an agent need a new element instance of the registered element.  
The first callback, <tt>creator</tt> is invoked when an agent need a new element instance of the registered element.  
<tt>create</tt> callback should return new element instance which matches the registered element specification.
The <tt>creator</tt> callback is invoked as a constructor.
 
The creator callback is expected to have <tt>HTMLElement.prototype</tt> in its prototype chain.
If it doesn't, agents set <tt>creator.prototype</tt> to <tt>HTMLElement.prototype</tt>.


<pre>
<pre>
Line 61: Line 66:
var element = document.createElement("element");
var element = document.createElement("element");
element.name = "x-comment";
element.name = "x-comment";
element.create = function() { return new HTMLElement("x-comment"); };
element.creator = function() { document.tags.bless(this); };
document.head.appendChild(element);
document.head.appendChild(element);
// Shorter form.
// Shorter form.
HTMLRegistrationElement.register("x-comment", function() { ... });
document.tags.register("x-comment", function() { ... });


<!-- Markup API -->
<!-- Markup API -->
<head>
<head>
   <element for="x-comment">
   <element name="x-comment">
     <script>
     <script>
     this.create = function() { return new HTMLElement("x-comment"); };
     document.tags.registering.creator = function() {
      document.tags.bless(this);
    };
     </script>
     </script>
   </element>
   </element>
Line 77: Line 84:
</pre>
</pre>


If the callback function has <tt>HTMLElement</tt> in its prototype chain,
Using class syntax, we could also write like this:
it is invoked as a constructor. Thus following two markup and one imperative examples have same meaning.


<pre>
<pre>


<!-- Markup 1 -->
<!-- Markup -->
   <element for="x-comment">
   <element name="x-comment">
     <script>
     <script>
     class Comment : HTMLElement {
     class Comment : HTMLElement {
       ....
       constructor() { HTMLElement.call(this); }
     };
     };


     this.create = Comment;
     document.tags.registering.creator = Comment;
     </script>
     </script>
   </element>
   </element>


<!-- Markup 2 -->
  <element for="x-comment">
    <script>
    class Comment : HTMLElement {
      ....
    };
    this.create = function() { return new Comment(); };
    </script>
  </element>


// Imperative
// Imperative
class Comment : HTMLElement {
class Comment : HTMLElement {
   ...
   constructor() { HTMLElement.call(this); }
};
};


HTMLRegistrationElement("x-comment", Comment);
document.tags.register("x-comment", Comment);
</pre>
 
If no <tt>creator</tt> callback is given, its default behavior is something like this:
 
<pre>
 
  <element name="x-comment">
    <script>
    document.tags.registering.creator = function() { document.tags.bless(this); };
    </script>
  </element>
 
</pre>
</pre>


If no <tt>create</tt> callback is given, its default behavior is something like this:
=== The <tt>creator</tt> attribute ===
 
A <tt>creator</tt> callback can also be set by the <tt>creator</tt> attribute on <tt>element</tt> element.
Once the attribute value is set, that is invoked as an expression.
The expression should return a function object, which is set as a <tt>creator</tt> callback.


<pre>
<pre>


   <element for="x-comment">
   <element name="x-comment" creator="Comment">
     <script>
     <script>
     var element = this;
     class Comment : HTMLElement {
    this.create = function() { return new HTMLElement(element.name); };
      ....
    };
     </script>
     </script>
   </element>
   </element>
Line 137: Line 150:
var element = document.createElement("element");
var element = document.createElement("element");
...
...
element.setup = function() { this.shadow = new ShadowRoot(this); ...; };
element.setup = function() {
  this.shadow = new ShadowRoot(this);
  ...;
};
...
...


<!-- Markup API -->
<!-- Markup API -->
<head>
<head>
   <element for="x-comment">
   <element name="x-comment">
     <script>
     <script>
     ...
     ...
     this.setup = function() {
     document.tags.registering.setup = function() {
       // "this" points the newly created element instance.
       // "this" points the newly created element instance.
       this.shadow = new ShadowRoot(this);  
       this.shadow = new ShadowRoot(this);  
Line 165: Line 181:
}
}
</pre>
</pre>
So if we define <tt>setup</tt> for the registered element, it will be called
as a <tt>setup</tt> callback.
  <element name="x-comment" creator="Comment">
    <script>
    class Comment : HTMLElement {
      setup() {
        // should be called.
      }
    }
    </script>
  </element>


== Styling ==
== Styling ==
Line 171: Line 201:
The style given by the element is a part of document stylesheet.  
The style given by the element is a part of document stylesheet.  
Authors can use the <tt>style</tt> element to provide the style for the registering element.
Authors can use the <tt>style</tt> element to provide the style for the registering element.


<pre>
<pre>


<head>
<head>
   <element for="x-comment">
   <element name="x-comment">
     <style>
     <style>
     x-comment {
     x-comment {
Line 187: Line 216:
</pre>
</pre>


== Shadow Tree construction ==
== Shadow Tree Construction and the <tt>template</tt> element ==


The <tt>element</tt> element helps to construct a shadow tree for newly created elements.
The <tt>element</tt> element helps to construct a shadow tree for newly created elements.
Line 198: Line 227:
<!-- Markup API -->
<!-- Markup API -->
<head>
<head>
   <element for="x-comment">
   <element name="x-comment">
     <template>
     <template>
       <div>...</div>
       <div>...</div>
Line 204: Line 233:
     <script>
     <script>
     ...
     ...
     this.setup = function(shadow) {
     document.tags.registering.setup = function(shadow) {
       // setup using the shadow instance.
       // Can setup using the shadow instance.
     };
     };
     </script>
     </script>
Line 216: Line 245:


<pre>
<pre>
[Callback=AcceptConstructor]
[Callback=ConstructorOnly]
interface HTMLRegistrationCreateCallback {
interface HTMLRegistrationCreatorCallback {
  HTMLElement create();
  HTMLElement create();
}:
};


[Callback]
[Callback]
interface HTMLRegistrationSetupCallback {
interface HTMLRegistrationSetupCallback {
  void setup(ShadowRoot shadow = null);
  void setup(ShadowRoot shadow = null);
}:
};


[Constructor]
[Constructor]
interface HTMLRegistrationElement {
interface HTMLRegistrationElement {
   attribute String name;
   attribute String name;
   attribute HTMLRegistrationCreateCallback create;
   attribute HTMLRegistrationCreatorCallback creator;
   attribute HTMLRegistrationSetupCallback setup;
   attribute HTMLRegistrationSetupCallback setup;
};
};
</pre>
</pre>
= <tt>HTMLRegistrar</tt> and <tt>document.elements</tt> =
<tt>HTMLRegistrar</tt> is an object oriented way to access registered element definitions.
It provides a keyed-collection like access to each registered element.
It also provides some utility method which helps idiomatic element registration and its use.
The object exists per document, and is accessible through the <tt>Document#elements</tt> property.
<pre>
[NoInterfaceObject]
interface HTMLRegistrar {
  getter HTMLRegistrationElement (DOMString name);
  void register(DOMString name, [Optional] HTMLRegistrationCreatorCallback creator);
  void bless(any self);
  readonly attribute HTMLRegistrationElement registering;
};
</pre>
=== The getter ===
The getter returns HTMLRegistrationElement whose <tt>name</tt> attribute
matches <tt>name</tt> parameter. unless returns null.
The getter can be used like this:
<pre>
var newCommentElement = document.elements["x-comment"].create();
</pre>
=== The <tt>register</tt> method ===
The <tt>HTMLRegistrar#register</tt> method is a shortcut for
following:
<pre>
var element = document.createElement("element");
element.name = name;
element.creator = creator;
document.head.appendChild(element);
</pre>
=== The <tt>bless</tt> method ===
=== The <tt>registering</tt> attribute ===
For each <tt>script</tt> block which is a child of <tt>element</tt> is executed,
The <tt>HTMLRegistar#registering</tt> is set to the <tt>element</tt> element.

Latest revision as of 01:37, 29 October 2011

Overview

With the Element Registration mechanism. page authors can tell new element names and its behavior to agents.

The element element and HTMLRegistrationElement

The HTMLRegistrationElement represents an author-registered element definition.

Here is the simplest example which registeres an element named "x-comment" whose instance implements HTMLElement


// Imperative API
var element = document.createElement("element");
element.name = "x-comment";
document.head.appendChild(element);
// Shorter form.
document.tags.register("x-comment");

<!-- Markup API -->
<head>
  <element name="x-comment"></element>
</head>

Scripting

The script element

The element element allows script elements in its children. These script blocks are executed over a global object. Before the evaluation, HTMLRegistrar.registering is set to the enclosing element element. These script blocks can be used for providing lifecycle callback definitions for the registering element.


<head>
  <element name="x-comment">
    <script>
     var shouldBeXComment = document.tags.registering.name;
    <script>
  </element>
</head>

The creator callback

The HTMLRegistrationElement provide a pair of lifecycle callbacks for the element instance creation including the tree construction.

The first callback, creator is invoked when an agent need a new element instance of the registered element. The creator callback is invoked as a constructor.

The creator callback is expected to have HTMLElement.prototype in its prototype chain. If it doesn't, agents set creator.prototype to HTMLElement.prototype.


// Imperative API
var element = document.createElement("element");
element.name = "x-comment";
element.creator = function() { document.tags.bless(this); };
document.head.appendChild(element);
// Shorter form.
document.tags.register("x-comment", function() { ... });

<!-- Markup API -->
<head>
  <element name="x-comment">
    <script>
    document.tags.registering.creator = function() {
      document.tags.bless(this);
    };
    </script>
  </element>
</head>

Using class syntax, we could also write like this:


<!-- Markup -->
  <element name="x-comment">
    <script>
    class Comment : HTMLElement {
       constructor() { HTMLElement.call(this); }
    };

    document.tags.registering.creator = Comment;
    </script>
  </element>


// Imperative
class Comment : HTMLElement {
  constructor() { HTMLElement.call(this); }
};

document.tags.register("x-comment", Comment);

If no creator callback is given, its default behavior is something like this:


  <element name="x-comment">
    <script>
    document.tags.registering.creator = function() { document.tags.bless(this); };
    </script>
  </element>

The creator attribute

A creator callback can also be set by the creator attribute on element element. Once the attribute value is set, that is invoked as an expression. The expression should return a function object, which is set as a creator callback.


  <element name="x-comment" creator="Comment">
    <script>
    class Comment : HTMLElement {
       ....
    };
    </script>
  </element>

The setup callback

The second lifecycle callback, setup is called after an instance creation. If the instance is created by the agent's tree construction, it will be called as a part of the "close" phase. On setup, the attributes and child elements for the element are already set by the agent. So this callback is useful for building its visual like the shadow tree.


// Imperative API
var element = document.createElement("element");
...
element.setup = function() {
  this.shadow = new ShadowRoot(this);
  ...;
};
...

<!-- Markup API -->
<head>
  <element name="x-comment">
    <script>
     ...
     document.tags.registering.setup = function() {
       // "this" points the newly created element instance.
       this.shadow = new ShadowRoot(this); 
       ...;
     };
    </script>
  </element>
</head>

If no setup callback is given by the author, agent invokes the default behavior. It is something like this:

function defaultSetup() {
  if (this.setup instanceof Function)
    this.setup();
}

So if we define setup for the registered element, it will be called as a setup callback.


 <element name="x-comment" creator="Comment">
   <script>
   class Comment : HTMLElement {
     setup() {
       // should be called.
     }
   }
   </script>
 </element>

Styling

The element element also allows the style element as its children. The style given by the element is a part of document stylesheet. Authors can use the style element to provide the style for the registering element.


<head>
  <element name="x-comment">
    <style>
    x-comment {
       color: gray;
    }
    </style>
  </element>
</head>

Shadow Tree Construction and the template element

The element element helps to construct a shadow tree for newly created elements. If element element has a template element as its child, The agent creates shadow tree from the template element before invoking the setup callback, then invokes it with the shadow root.


<!-- Markup API -->
<head>
  <element name="x-comment">
    <template>
      <div>...</div>
    </template>
    <script>
     ...
     document.tags.registering.setup = function(shadow) {
       // Can setup using the shadow instance.
     };
    </script>
  </element>
</head>

HTMLRegistrationElement interface

[Callback=ConstructorOnly]
interface HTMLRegistrationCreatorCallback {
 HTMLElement create();
};

[Callback]
interface HTMLRegistrationSetupCallback {
 void setup(ShadowRoot shadow = null);
};

[Constructor]
interface HTMLRegistrationElement {
  attribute String name;
  attribute HTMLRegistrationCreatorCallback creator;
  attribute HTMLRegistrationSetupCallback setup;
};

HTMLRegistrar and document.elements

HTMLRegistrar is an object oriented way to access registered element definitions. It provides a keyed-collection like access to each registered element. It also provides some utility method which helps idiomatic element registration and its use.

The object exists per document, and is accessible through the Document#elements property.



[NoInterfaceObject]
interface HTMLRegistrar {
  getter HTMLRegistrationElement (DOMString name);
  void register(DOMString name, [Optional] HTMLRegistrationCreatorCallback creator);
  void bless(any self);
  readonly attribute HTMLRegistrationElement registering;
};

The getter

The getter returns HTMLRegistrationElement whose name attribute matches name parameter. unless returns null.

The getter can be used like this:


var newCommentElement = document.elements["x-comment"].create();

The register method

The HTMLRegistrar#register method is a shortcut for following:


var element = document.createElement("element");
element.name = name;
element.creator = creator;
document.head.appendChild(element);

The bless method

The registering attribute

For each script block which is a child of element is executed, The HTMLRegistar#registering is set to the element element.