Re: [Custom Elements] They are globals.

2016-04-12 Thread /#!/JoePea
On Mon, Apr 11, 2016 at 4:44 PM, Ryosuke Niwa  wrote:
> You'd have to instead write it as "new SomeClass(this.shadowRoot)" and then 
> (1) needs to be modified as: `super(..arguments)` to pass the argument along 
> to the HTMLElement constructor.

For sure, similar to the examples in the GitHub issue. :]

React doesn't encourage the instantiation of element classes directly.
What if browsers did that too and threw errors whenever someone tried
to instantiate a class using `new` that extended from any of the
native classes:

```js
import {SomeElement} from 'some-library'

// this would cause a `TypeError: Illegal constructor` or something
let el = new SomeElement()

// does not return a constructor
shadowRoot1.registerElement('any-name', SomeElement)

// only this instantiation method is allowed
let el1 = shadowRoot1.createElement('any-name')

// we can use the same class in a different component where
//  is already defined but isn't backed by SomeElement
shadowRoot2.registerElement('other-name', SomeElement)
let el2 = shadowRoot2.createElement('other-name')

// maybe this should throw an error instead of returning
// HTMLUnknownElement, otherwise it might hide the obvious
// human error instead teaching the developer
shadowRoot1.registerElement('a-name', SomeElement)
let el3 = shadowRoot2.createElement('a-name')

// this would cause an error because el1 was created from
// shadowRoot1 so el1 doesn't work in shadowRoot2
shadowRoot2.appendChild(el1)
```

ShadowDOM is still evolving and the Web Component spec hasn't
officially settled, so I think there's a good opportunity here for the
web to be more helpful by throwing errors and not being ambiguous as
for example in `document.createElement('ths-has-a-typo')`.

TLDR - ShadowDOM could be the encapsulation of HTML to Custom Elements
as JSX is the encapsulation of HTML to React Components (in a morphed
way, as obviously the mechanics are different).

Here's what a small component might look like:

```js
//  --- HandyForm.js
import AwesomeButton from './AwesomeButton'
import { FancyForm, FancyInput } from './FancyForm'

export default
class HandyForm extends HTMLElement {
constructor() {
this.root = this.createShadowRoot()
this.root.registerElement('', AwesomeButton)
this.root.registerElement('', FancyForm)
this.root.registerElement('', FancyInput)

const frag = document.createDocumentFragment()
frag.innerHTML = `


 



`
this.root.appendChild(frag)
}

static get observedAttributes() { return [ ... ] }

connectedCallback() { ... }
disconnectedCallback() { ... }
attributeChangedCallback() { ... }
}

//  --- app.js
import HandyForm from './HandyForm'

// elements registered on the document won't cross into shadow roots
document.registerElement('handy-form', HandyForm)
document.body.appendChild(document.createElement('handy-form'))
```

- Joe

/#!/JoePea



Re: [Custom Elements] They are globals.

2016-04-11 Thread Ryosuke Niwa

> On Apr 11, 2016, at 2:29 PM, /#!/JoePea  wrote:
> 
> What if custom elements can be registered on a shadow-root basis, so
> that the author of a Custom Element (one that isn't registered by
> default) can register a bunch of elements that it's shadow root will
> use, passing constructors that the author code may have imported from
> anywhere. Then that same author simply exports the custom element
> (does not register it) for a following author to use. The following
> author would import that custom element, then register it with the
> shadow roots of his/her new custom elements, and thus the cycle
> continues, with registered elements defined on shadow roots on a
> per-custom-element basis, without those elements ever being registered
> to some global registry.
> 
> ```
> // library code
> import SomeClass from './somewhere'
> 
> export default
> class AuthorElement extends HTMLElement {
>constructor() {
>this.shadowRoot.defineElement(
>'authors-should-call-this-anything-they-want',
>SomeClass
>)
>// ...
>}
> }
> ```

Now, let's say you do "new SomeClass" now, and SomeClass is defined as follows:

```js
class SomeClass extends HTMLElement {
constructor()
{
super(); // (1)
}
}
```

When HTMLElement's constructor is involved in (1), it needs to construct an 
element by the name of "authors-should-call-this-anything-they-want".  However, 
it has no idea to which shadow root or document it belongs.  The fundamental 
here is that any construction of new element now needs to specify the shadow 
root or document for which it is created so simple "new SomeClass" would not 
work.  You'd have to instead write it as "new SomeClass(this.shadowRoot)" and 
then (1) needs to be modified as: `super(..arguments)` to pass the argument 
along to the HTMLElement constructor.

- R. Niwa




Re: [Custom Elements] They are globals.

2016-04-11 Thread /#!/JoePea
I get what you mean about the behaviors defined from classes that
exist in the scope, in React. The really great thing about React is
the ability to compose a class by using multiple classes to return the
render spec. One of React's strong-points at a high level is it offers
encapsulation where the components used in the HTML (which don't map
directly to the actual DOM result that the browser is finally given)
are specific to the component, encapsulated there.

The current methods of encapsulation that I see with normal HTML are
documents and shadow roots, where shadow roots seem to be closer to
React than documents because the top-level HTML that we inspect in the
DOM may not be what is actually rendered (like with React), although
the implementation is completely different, but at a high level shadow
dom has that small similarity with React.

So, to make code-reuse as possible as it is with React (due to React's
aforementioned encapsulation and use of the JavaScript scopefor it's
"HTML elements"), maybe the Custom Element API can take advantage of
ShadowDOM instead of documents?

What if custom elements can be registered on a shadow-root basis, so
that the author of a Custom Element (one that isn't registered by
default) can register a bunch of elements that it's shadow root will
use, passing constructors that the author code may have imported from
anywhere. Then that same author simply exports the custom element
(does not register it) for a following author to use. The following
author would import that custom element, then register it with the
shadow roots of his/her new custom elements, and thus the cycle
continues, with registered elements defined on shadow roots on a
per-custom-element basis, without those elements ever being registered
to some global registry.

```
// library code
import SomeClass from './somewhere'

export default
class AuthorElement extends HTMLElement {
constructor() {
this.shadowRoot.defineElement(
'authors-should-call-this-anything-they-want',
SomeClass
)
// ...
}
}
```

Then, finally, the top-level app author can register the top-most
custom elements using an API similar to current, in the top-level
namespace (global scope) for the window or document.

```
// app code
import AuthorElement from './AuthorElement'
document.defineElement(
'any-name-the-app-author-wants',
SomeClass
)
```

So, basically, inaddition to global/window-or-document-based elements
there'd also be shadow root definitions for encapsulation, since
shadow roots are to custom elements as what (slightly stretched) JSX
specs are to React components.

An important part of the Custom Element API would be that Custom
Element authors should never register their Custom Elements globally,
only export them for the user of their custom element to register
them. The final top-level app author would register only the elements
that will be used in the top-level HTML of the app, all other elements
already registered in their shadow roots by the authors of each
element.

Something like this would create a development environment much more
similar to React, having encapsulation and making code more re-usable.
The only downside of this approach would be the need to manually
register elements per-shadow-root, not just importing them (in react,
importing is enough, as the JSX uses the JavaScript scope as the
element registry).

On a side note, interestingly, template string tag functions would let
us take advantage of JavaScript scope directly without build tools:

```
import SomeClass from './SomeClass'
import OtherClass from './OtherClass'

html`
  
  <${SomeClass}>
  <${OtherClass}>
  
  
  
`
```
/#!/JoePea


On Mon, Apr 11, 2016 at 11:25 AM, Domenic Denicola  wrote:
> I think you are being misled by a superficial similarity with React's JSX.
>
> JSX's `` desugars to `React.createElement(Foo)`, which creates a `` 
> element with some of its behavior derived from the `Foo` class, found in 
> JavaScript lexical scope. The `Foo` token has no impact on the DOM tree.
>
> Custom elements' `` is completely unlike that. In that case, `x-foo` 
> is a tag name, and a full participant in the DOM tree structure. It affects 
> CSS selector matching, APIs like querySelector and getElementsByTagName, and 
> more. It's not just a div.
>
> As Ryosuke notes, it's very hard to imagine how "scoped tag names" would 
> work. Both for implementation reasons, like the HTMLElement constructor, but 
> also for cases like CSS or the DOM APIs, which operate fundamentally on a 
> global mapping.
>



RE: [Custom Elements] They are globals.

2016-04-11 Thread Domenic Denicola
I think you are being misled by a superficial similarity with React's JSX.

JSX's `` desugars to `React.createElement(Foo)`, which creates a `` 
element with some of its behavior derived from the `Foo` class, found in 
JavaScript lexical scope. The `Foo` token has no impact on the DOM tree.

Custom elements' `` is completely unlike that. In that case, `x-foo` is 
a tag name, and a full participant in the DOM tree structure. It affects CSS 
selector matching, APIs like querySelector and getElementsByTagName, and more. 
It's not just a div.

As Ryosuke notes, it's very hard to imagine how "scoped tag names" would work. 
Both for implementation reasons, like the HTMLElement constructor, but also for 
cases like CSS or the DOM APIs, which operate fundamentally on a global mapping.



Re: [Custom Elements] They are globals.

2016-04-11 Thread Ryosuke Niwa

> On Apr 11, 2016, at 9:02 AM, /#!/JoePea  wrote:
> 
> Is it possible to take an approach more similar to React where Custom 
> Elements aren't registered in a global pool? What if two libraries we'd like 
> to use define elements of the same name, and we wish to import these via 
> `import` and not modify the source code of our dependencies?
> 
> I don't really see the solution yet (if any), since the browser needs to know 
> about the elements in order to make them work.
> 
> Any thoughts? Is a more encapsulated approach possible?

We discussed a similar issue related to having multiple documents per global 
object: https://github.com/w3c/webcomponents/issues/369 


The problem here is that HTMLElement constructor, which is involved as a super 
call in a custom element constructor, cannot determine which set of custom 
elements to use because it doesn't get any contextual information about where 
the element is constructed.

- R. Niwa



[Custom Elements] They are globals.

2016-04-11 Thread /#!/JoePea
Hello,

Is it possible to take an approach more similar to React where Custom
Elements aren't registered in a global pool? What if two libraries we'd
like to use define elements of the same name, and we wish to import these
via `import` and not modify the source code of our dependencies?

I don't really see the solution yet (if any), since the browser needs to
know about the elements in order to make them work.

Any thoughts? Is a more encapsulated approach possible?

Regards,
- Joe


-- 
/#!/JoePea