Giter Site home page Giter Site logo

webreflection / document-register-element Goto Github PK

View Code? Open in Web Editor NEW
1.1K 39.0 119.0 2.11 MB

A stand-alone working lightweight version of the W3C Custom Elements specification

License: ISC License

Makefile 1.80% HTML 2.13% JavaScript 96.06%
polyfill customelements custom element v1 v0

document-register-element's Introduction

Deprecated

This polyfill has served the Web well in the last 6 years, but it's time to use the V1 only polyfill, which includes custom elements builtin extends like this one did before.

Such polyfill is in npm as @ungap/custom-elements and it does all features detections for every browser without any need to worry about anything.

<script src="//unpkg.com/@ungap/custom-elements"></script>
<script>
// alternatively, if bundlers are around
import '@ungap/custom-elements';
// or
require('@ungap/custom-elements');
</script>

However, if all you need is custom elements without builtin extends, a module also used by @ungap/custom-elements , @webreflection/custom-elements-no-builtin is your stop.

<script>
if(!self.customElements)
  document.write('<script src="//unpkg.com/@webreflection/custom-elements-no-builtin"><\x2fscript>');
</script>

Alternatively, if it's a ponyfill hat you are after, see @webreflection/custom-elements instructions, as this module is already exported as ponyfill.


build status donate Backers on Open Collective Sponsors on Open Collective

Announcement

Please use the modern version of this polyfill instead, which includes the following features:

  • no constructor caveats, everything works like in Chrome or Firefox
  • better performance, only defined builtin gets observed, thanks to qsa-observer
  • better memory handling: no leaks, and less operations
  • better ShadowDOM integration: builtin extends are observed within ShadowDOM nodes, either opened or closed

A stand-alone lightweight version of Custom Elements V1 based on top, and compatible with, the battle-tested Custom Elements V0, already used in production with projects such as Google AMP HTML ⚡ and others.

Important: How To Migrate To Native V1

Projects based on this polyfill should consider migrating to V1 API, which is natively available for Safari, Chrome, Firefox, and soon Edge too.

Where built in extends are not possible, you can use my latest built-in-element polyfill instead, which will leave natively working implementations untouched, and will apply the minimal amount of patches to native V1 without built-in elements (only Safari to date).

How To Polyfill Custom Elements V1

This is a bullet-proof way to bring in Custom Elements V1 when needed.

<script>this.customElements||document.write('<script src="//unpkg.com/document-register-element"><\x2fscript>');</script>
<script src="//unpkg.com/@ungap/custom-elements-builtin"></script>

Don't worry though, only very old browsers will pass through that document.write, preserving its 20yo tested nature, while no modern browser will ever complain.

Stick above sequence of scripts on top of any of your pages, and you'll see that only very old browsers will download this polyfill, while others will load less than 1k, delivering Custom Elements with 100% native performance.

If you are bundling instead all the code, consider decoupling DRE bundling a part, or perform the same customElements check on the window and bring in only the right polyfill.

What About Shadow DOM ?

This polyfill, as well as built-in-element one, are about Custom Elements that are a specification a part.

If you need for some reason Shadow DOM, I suggest you to look at attach-shadow "poorlyfill", which provides most basic/needed mechanism to create sandboxed components.

You can try other polyfills around the web too but, if I were you, I'll stay away from Shadow DOM where it's not natively supported because other polyfills are heavier, less compatible, and yet not 100% reliable.

Transpilers VS 1.9

The version 1.9 of this polyfill does not patch browsers with full native support for Custom Elements, as anyone would expect from a polyfill based on features detection.

However, if your transpiler transforms native ES2015 classes into something incompatible, like TypeScript does in this case, you need to update, change, or better configure your tools to support proper classes.

Babel 7 should've solved this in core, so use Babel 7 if you need transpilers.

How to avoid CE Built In

Since version 1.6 the ponyfill flag can be either a string, representing the ponyfill type such "auto" or "force", or an object, with the following shape:

installCE(global, {
  type: 'force' || 'auto' (default),
  noBuiltIn: true (default undefined / false)
});

If you set noBuiltIn to true, the V1 API will be polyfilled where needed.

No extra checks and patches will be applied to make custom elements built-in work.

New Ponyfill in 1.3

As discussed in issue #86 there is currently no way to require document-register-element polyfill without automatic feature detection and possible global context pollution.

Since there could be some very specific case when the browser should be force-patched, the pony version of the module will not attempt to feature detect anything and it will only enrich the environment once invoked.

const installCE = require('document-register-element/pony');

// by default, the second argument is 'auto'
// but it could be also 'force'
// which ignores feature detection and force
// the polyfill version of CustomElements
installCE(global, 'force');

What's new in Custom Elements v1

The ability to extend by simply defining classes:

// create a class with custom methods
// overrides, special behavior
class MyGreetings extends HTMLElement {
  show() {
    alert(this.textContent);
  }
}

// define it in the CustomElementRegistry
customElements.define('my-greetings', MyGreetings);

It is also possible to extend native components, as written in specs.

// extends some different native constructor
class MyButton extends HTMLButtonElement {}

// define it specifying what's extending
customElements.define('my-button', MyButton, {extends: 'button'});

// <button is="my-button">click me</button>
document.body.appendChild(
  new MyButton
).textContent = 'click me';

Special methods are also slightly different from v0:

  • the constructor is invoked instead of the createdCallback one
  • connectedCallback is the new attachedCallback
  • disconnectedCallback is the new detachedCallback
  • attributeChangedCallback is sensitive to the public static list of attributes to be notified about
class MyDom extends HTMLElement {
  static get observedAttributes() {
    return ['country'];
  }
  attributeChangedCallback(name, oldValue, newValue) {
    // react to changes for name
    alert(name + ':' + newValue);
  }
}
customElements.define('my-dom', MyDom);
var md = new MyDom();
md.setAttribute('test', 'nope');
md.setAttribute('country', 'UK'); // country: UK

V1 Caveat

The current standard cannot possibly be polifilled "1:1" with vanilla JavaScript because procedurally created instances need an upgrade. If the constructor is needed to setup nodes, there are two solutions:

Upgrading the constructor context

class MyElement extends HTMLElement {
  // the self argument might be provided or not
  // in both cases, the mandatory `super()` call
  // will return the right context/instance to use
  // and eventually return
  constructor(...args) {
    const self = super(...args);
    self.addEventListener('click', console.log);
    // important in case you create instances procedurally:
    // var me = new MyElement();
    return self;
  }
}

Skipping the caveat through extends

// base class to extend, same trick as before
class HTMLCustomElement extends HTMLElement {
  constructor(...$) { const _ = super(...$); _.init(); return _; }
  init() { /* override as you like */ }
}

// create any other class inheriting HTMLCustomElement
class MyElement extends HTMLCustomElement {
  init() {
    // just use `this` as regular
    this.addEventListener('click', console.log);
    // no need to return it
  }
}

Inherited V0 Caveats

Please keep in mind old gotchas with innerHTML or other caveats are still valid.

How

npm install document-register-element will put build/document-register-element.js inside node_modules/document-register-element/ of your project.

If you're working with a tool like Browserify, Webpack, RequireJS, etc, you can import the script at some point before you need to use the API.

import 'document-register-element' // ES2015
// or
require('document-register-element') // CommonJS
// or
define(['document-register-element'], function() {}) // AMD

If you're not using a module system, just place node_modules/document-register-element/build/document-register-element.js somewhere where it will be served by your server, then put

<script src="/path/to/document-register-element.js"></script>

in your head element and you should be good to go.

via CDN

Many thanks to cdnjs for hosting this script. Following an example on how to include it.

<script
  src="//cdnjs.cloudflare.com/ajax/libs/document-register-element/1.13.0/document-register-element.js"
>/* W3C Custom Elements */</script>

Tested On

The live test page is here, containing all tests as listed in the test file.

The following list of desktop browsers has been successfully tested:

  • Chrome
  • Firefox
  • IE 8 or greater (please read about IE8 caveats)
  • Safari
  • Opera

The following list of mobile OS has been successfully tested:

  • iOS 5.1 or greater
  • Android 2.2 or greater
  • FirefoxOS 1.1 or greater
  • KindleFire 3 or greater
  • Windows Phone 7 or greater
  • Opera Mobile 12 or greater
  • Blackberry OS 7* and OS 10
  • webOS 2 or LG TV
  • Samsung Bada OS 2 or greater
  • NOKIA Asha with Express Browser

The good old BB OS 7 is the only one failing the test with className which is not notified as attributeChanged when it's changed. This means BB OS 7 will also fail with id, however changing id at runtime has never been a common or useful pattern.

TL;DR does it work ?

If you see the first clock ticking, the TL;DR answer is yes.

V0 Usage Example

A basic HTML example page

<!DOCTYPE html>
<html>
<head>
  <title>testing my-element</title>
  <script src="js/document-register-element.js"></script>
  <script src="js/my-element.js"></script>
</head>
<body>
  <my-element>
    some content
  </my-element>
</body>

with the following my-element.js content

var MyElement = document.registerElement(
  'my-element',
  {
    prototype: Object.create(
      HTMLElement.prototype, {
      createdCallback: {value: function() {
        console.log('here I am ^_^ ');
        console.log('with content: ', this.textContent);
      }},
      attachedCallback: {value: function() {
        console.log('live on DOM ;-) ');
      }},
      detachedCallback: {value: function() {
        console.log('leaving the DOM :-( )');
      }},
      attributeChangedCallback: {value: function(
        name, previousValue, value
      ) {
        if (previousValue == null) {
          console.log(
            'got a new attribute ', name,
            ' with value ', value
          );
        } else if (value == null) {
          console.log(
            'somebody removed ', name,
            ' its value was ', previousValue
          );
        } else {
          console.log(
            name,
            ' changed from ', previousValue,
            ' to ', value
          );
        }
      }}
    })
  }
);

Why

I wrote a couple of blog posts about this polyfill, and here's the quick summary:

  • document-register-element.js is a stand alone polyfill which aims to support as many browsers as possible, without requiring extra dependencies at all, all in about 5KB minified and gzipped.

Add if you want the dom4 normalizer, and you'll find yourself in a modern DOM environment that works reliably with today's browsers, with an eye always open on performance.

Common Issues + Caveat

Here a list of gotchas you might encounter when developing CustomElement components.

HTML{TABLE|ROW|INPUT|SELECT|others...}Element

As described in issue 6 it's not possible to fully inherit a table, input, select, or other special element behaviors.

// This will NOT work as expected
document.registerElement(
  'my-input',
  {
    prototype: Object.create(
      HTMLInputElement.prototype
    )
  }
);

var mi = document.createElement('my-input');

The correct way to properly implement a custom input that will be also backward compatible is the following one:

// This will NOT work as expected
document.registerElement(
  'my-input',
  {
    extends: 'input', // <== IMPORTANT
    prototype: Object.create(
      HTMLInputElement.prototype
    )
  }
);

// how to create the input
var mi = document.createElement(
  'input',    // the extend
  'my-input'  // the enriched custom definition
);

Another approach is to use just a basic HTMLElement component and initialize its content at runtime.

document.registerElement(
  'my-input',
  {
    prototype: Object.create(
      HTMLElement.prototype,
      {
        createdCallback: {value: function () {
          // here the input
          this.el = this.appendChild(
            document.createElement('input')
          );
        }}
      }
    )
  }
);

var mi = document.createElement('my-input');

In this case every method that wants to interact with the input will refer this.el instead of just this.

Using innerHTML

In order to avoid huge performance impact, native behavior overwrite problems and incompatibilities, there is now a helper script, which aim is to make off-line custom elements creation possible using template strings instead of needing manual document.createElement replacements.

The helper is a simple innerHTML function that returns the given node, after setting innerHTML and, in case the polyfill is used, initialize nodes.

This helper is needed in order to be aligned with native implementations, but please remember that createdCallback could be asynchronous, even if triggered ASAP after injecting HTML through this function.

Changing the style property

If you change the style property via node.style.cssText or node.style.backgroundColor = "red" this change will most likely reflect through node.getAttribute("style").

In order to prevent footguns inside attributeChangedCallback invocations causing potential stack overflows, the style property has been filtered starting from version 0.1.1, also reflecting current native implementation where changing this special property won't invoke the callback.

(yes, even using node.setAttribute("style", "value") that you shouldn't ... just use node.style.cssText = "value" instead)

About IE8

Starting from version 0.2.0 there is an experimental support for IE8. There is a specific file that needs to be loaded in IE8 only upfront, plus a sequence of polyfills that will be simply ignored by every browser but downloaded in IE8.

Please check base.html file in order to have a basic model to reuse in case you want to support IE8.

All tests pass and there is a map component example that already works in IE8 too.

Remember there are few things to consider when IE8 is a target but since it didn't cost many bytes to have it in, I've decided to merge the logic and maintain only one file that will work in IE8 too.

IE8 caveats

  • it's IE8
  • all operations are batched and eventually executed ASAP but asynchronously. This behavior is closer to native Mutation Observers but might have some extra glitch in rendering time
  • className is right now the only special attribute that reacts. Others might be implemented in the dre-ie8-upfront-fix.js file.
  • in order to have node reacting to attributes changes, these must be live on the DOM
  • if you are using extends when create a custom element, remember to minify the production code or wrap such reserved word in quotes

Contributors

This project exists thanks to all the people who contribute. [Contribute].

Backers

Thank you to all our backers! 🙏 [Become a backer]

Sponsors

Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor]

document-register-element's People

Contributors

andersdjohnson avatar chiefcll avatar dandv avatar dnicolson avatar gustafnk avatar monkeywithacupcake avatar opinel avatar rgbkrk avatar rozwell avatar tomalec avatar trusktr avatar webreflection avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

document-register-element's Issues

Can i defer registration of an element?

Hi, I love custom elements and this project that allow us to support they even in ie8 :)
I'd like to know if i can write my custom markup first and then register all my custom elements.
For instance, loading a <script async> tag at the bottom of the page where my elements will be registered.

Thanks
Marco

Elements in ShadowDOM not being instantiated?

I'm not sure if it's a problem with your v0, or the Chrome v0 API, but I've noticed that when I put my custom elements inside a shadow tree, the createdCallbacks don't get called.

(I haven't migrated to v1 yet.)

For example, I've got code like the following (assume that the motor-node and motor-scene elements have created callbacks that log to console "created!":

import React from 'react'
import ReactDOM from 'react-dom'
import sleep from 'awaitbox/timers/sleep'
import startup from 'awaitbox/meteor/startup'
import 'infamous/motor-html' // this imports document-register-element

await startup()

ReactDOM.render(
    <motor-scene id="scene">
        <motor-node id="distributedNode">
        </motor-node>
    </motor-scene>,
    document.querySelector('#app-root')
)

await sleep(500)

let scene = document.querySelector('#scene')

let root = scene.createShadowRoot()

root.innerHTML = `
    <motor-node id="innerNode">
        <content></content>
    </motor-node>
`

console.log(root)

As you see, there are three total motor- elements, one of which is inside a shadow tree. createdCallback is only called for the ones outside the tree, and never for the motor-node inside the shadow tree.

Reproduction

git clone [email protected]:trusktr/site --branch document-register-element-issue-68 --single-branch
cd site/meteor-app
npm install
meteor

Requires Meteor installed from http://meteor.com.

IE <= 10 doesn't fire attributeChangedCallback for initial values if it's an empty string (boolean attribute)

See http://codepen.io/treshugart/pen/bZXgYB (tested in IE10, should be the same in IE9).

Basically, IE has a bug where the mutation event prevValue is an empty string on the initial set. If your new value is also an empty string the change handler doesn't fire because they're equal. The use-case here is when setting boolean attributes: element.setAttribute('test', '').

I believe this exists in both IE 9 and 10, but unsure if it will occur in your IE8 fill. When we had this built into Skate awhile back, the fix was to keep track of the previous value manually and compare it to the one we had on record instead of relying on the one from the event.

Issue in IE8.JS not able to load component

Hello,

      In IE8.js file getting script error which is not loading element in IE8. Referred sample clock example [http://webreflection.github.io/document-register-element/test/examples/x-clock.html]. Can some one please help out with this.

Thanks.

Delayed createdCallback

Sorry if this is a non-issue, custom elements and this excellent polyfill is quite new (and exciting) to me.

I'm trying to use this in a Backbone app, where each view has its own template, and that template might contain some custom element (the view then also makes sure that the custom element is registered first).

I noticed that when I insert the template with the custom element in the dom, there is a slight delay before the createdCallback even exists on the custom element and I can communicate with the properties of the custom element.
This does not happen in Chrome where registerElement is natively supported. It also happens when using the polyfill from Polymer though.

Any idea why this happens? This makes it a bit harder to work with, and the difference between the native version and the polyfill is a bit worrisome.

Here is a test case:
http://jsfiddle.net/mt06sykv/

Thanks in advance for any reply.

Einar

Script breaks in Firefox when uglified (using uglify-js2)

When the script has been minified using uglify-js2, Firefox (v41) throws the following error:

SyntaxError: in strict mode code, functions may be declared only at top level or immediately within another function

When I remove the "use strict" statement, it fails with this:

ReferenceError: setupNode is not defined

Both times it continues to work on Chrome.

Running npm test

Hi,

Thanks for the great polyfill. :-)
When running make for the project, there are no tests executed. Is there a twist to make the test work from the command line or are they currently not working?

Custom Child Elements for Custom Elements

Hi,

I'd like to better understand the right way to create custom elements with custom child elements using this tool.

For example, say I want to have the following code:

<x-foo>
    <x-foo-child>child content</x-foo-child>
</x-foo>

Do I just call document.registerElement for both custom elements?

Thanks.

Type extension doesn't work for SVG elements

Hi, for an unknown reason the following spec (which passes on Chrome) doesn't pass with the polyfill.

it("creates a custom SVG element", function() {

    var MegaCircle = document.registerElement('mega-circle', {
      prototype: Object.create(SVGCircleElement.prototype),
      extends: 'circle'
    });

    var element = document.createElementNS("http://www.w3.org/2000/svg",  "circle",  "mega-circle");
    expect(element instanceof MegaCircle).toBe(true);

  });

Using the polyfill, element still an instance of SVGCircleElement instead.

Bower package

Hi,

would be great if you register your repo as bower component.

CDNJS

can you add a dre.min.js to the cdn instead of just the normal unformatted minified version when updating the CDNJS repo

Some tests apparently fail under Opera/windows?

Awesome job! Definitively looking forward using your polyfill.

I noticed one of the platform available through modern.ie for testing report some errors. Your might be interested in looking at the results.
Not that I am interested in this particular platform but it might be worth looking.

IE8 errors when running the x-clock example with latest sources of ie8.js and dom4.js

Thanks a lot for this library; it's really made life easier when trying to build cross-browser web components.

I have a specific issue when I run your x-clock example using recent sources of ie8.js (0.2.9), dom4.js (1.5.1), and document-register-element.js (0.5.0). On IE8, I get the following errors:

Error Message

Here is the HTML code:

<!DOCTYPE html>
<html>
<head>
    <title>x-clock</title>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0,minimal-ui">
    <!--[if lte IE 9]><script>(function(f){window.setTimeout=f(window.setTimeout);window.setInterval=f(window.setInterval)})(function(f){return function(c,t){var a=[].slice.call(arguments,2);return f(function(){c.apply(this,a)},t)}});</script><![endif]-->
    <!--[if IE 8]><script src="//cdnjs.cloudflare.com/ajax/libs/ie8/0.2.9/ie8.js"></script><![endif]-->
    <script src="//cdnjs.cloudflare.com/ajax/libs/dom4/1.5.1/dom4.js"></script>
    <!--[if IE 8]><script src="//cdnjs.cloudflare.com/ajax/libs/document-register-element/0.3.0/dre-ie8-upfront-fix.js"></script><![endif]-->
    <script src="//cdnjs.cloudflare.com/ajax/libs/document-register-element/0.5.0/document-register-element.js"></script>
    <!--[if IE 8]>
    <script src="//cdnjs.cloudflare.com/ajax/libs/es5-shim/4.1.0/es5-shim.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/es5-shim/4.1.0/es5-sham.js"></script>
    <![endif]-->
    <script src="js/restyle.js"></script>
    <!-- x-clock Custom Element -->
    <script src="js/x-clock.js"></script>
</head>
<body>
<h3>dynamic</h3>
<x-clock>10:10:30</x-clock>
<h3>fixed</h3>
<x-clock hour="10" minute="10" second="30">10:10:30</x-clock>
</body>
</html>

Any way to fix this?

`target.getAttribute` is not defined

Even with some of the simplest components, I keep getting the following error (paraphrase from Firefox console although it seems to happen in all browsers):

TypeError: target.getAttribute is not a function
getTypeIndex()                document-register-element.max.js:422
verifyAndSetupAndAction()     document-register-element.max.js:520
loopAndVerify()               document-register-element.max.js:397
onReadyStateChange()          document-register-element.max.js:469

The following is a sample of my component:

let Star = document.registerElement('x-star', {
  prototype: {
    createdCallback() {
      console.log('created')
    },
    attachedCallback() {
      console.log('attached')
    }
  }
})

Extends the HTMLTableElement and HTMLTableRowElement

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)

"Cannot read property 'create' of undefined"

Привет!
Если элемент сперва использован в разметке, а затем (ниже) определён, происходит ошибка: https://yadi.sk/i/_vIUE9MjuWRNH . Если поменять местами -- ошибка исчезает.
v1.0.7, в 0.5.4 её не было.
Спасибо.

attributeChangedCallback isn't called for initial attribute of a created element

Hi,

When an instance of a custom element is created it seems the specs (4.3.8) say that attributeChangedCallback() have to be fired for all initial attributes.

e.g.
<my-dom test="mytest">test</my-dom>

class MyDom extends HTMLElement {

  static get observedAttributes() {
    return ['test'];
  }
  attributeChangedCallback(name, oldValue, newValue) {
    console.log(name + ':' + newValue);
  }
}
customElements.define('my-dom', MyDom);

should fire attributeChangedCallback() when the element is parsed and created.

And as you can see in this jsfiddle it's not the case.

I'm on chrome beta but it's the same in firefox.

And you can test the same code in chrome canary with custom element enabled here

Thanks

Custom elements are not initialized until they are appended to the DOM

When a custom element is instantiated outside the DOM, the polyfill fails to run these callbacks.
They are only called when the element is inserted into the DOM.
Native implementations run these callbacks even outside the document.

document.registerElement('my-element', {
    prototype: Object.create(
        HTMLElement.prototype, {
            createdCallback: {
                value: function () {
                    console.log('created');
                }
            },
            attachedCallback: {
                value: function () {
                    console.log('attached');
                }
            }
        })
});

var div = document.createElement("div");
var myElement = document.createElement("my-element");
// nothing
div.innerHTML = "<my-element></my-element>";
// nothing
document.body.appendChild(div);
// created
// attached

Element constructor produces incorrect is attribute and doesn't call attachedCallback in Firefox

Creating elements by constructor incorrectly produces an is attribute and doesn't call attachedCallback in Firefox. (It works correctly in Safari, and I didn't test IE.)

var ctor = document.registerElement("my-element", {});
new ctor();

produces <my-element></my-element> in Safari, but <my element is="false"></my-element> in Firefox. After some digging, this is because the constructor returned by document.registerElement calls document.createElement("my-element", false) in Firefox, and that false argument is then stringified into the is attribute.

More seriously, the bad is attribute causes attachedCallback to never fire.

Tested in document-register-element 4.0 and Firefox 37-38. (I checked the tests, and there don't appear to be any unit tests covering constructors or attachedCallback.)

createdCallback is called twice in IE

Hi,

I've just noticed this strange behaviour in IE 10 (it also happens in the "IE 9 mode" of the same browser). Here's a quick snippet that illustrates the issue:

 <!doctype html>
 <body>
   <script src="document-register-element.js"></script>
   <script>
     var testProto = Object.create(HTMLElement.prototype);

     testProto.createdCallback = function () {
       var button = document.createElement('button');
       button.appendChild(document.createTextNode('Foo'));
       this.appendChild(button);
     };

     document.registerElement('x-test', { prototype: testProto });
     document.body.appendChild(document.createElement('x-test'));
   </script>
 </body>

Once rendered, the x-test element contains two button elements instead of one. createdCallback is actually called twice. The same code behaves as expected in chrome and firefox.

Unable to run mocha/phantomjs with AMD version of document-register-element

I used grunt-mocha to test my custom element and it fails due to "Warning: ReferenceError: Can't find variable: REGISTER_ELEMENT" when using AMD version of document-register-element.amd.js

I need to add the following to the script to avoid the issue:

var REGISTER_ELEMENT = 'registerElement';
if (REGISTER_ELEMENT in document) {
return;
}

Webpack out-of-box compatibility

It seems Webpack tries to build in its AMD shim when bundling the .max version in your bundle/ directory. I'm assuming that it finds what it thinks are references to define being used, but I haven't singled out exactly which lines of code are causing it to do that yet.

Failed to execute 'registerElement' on 'Document': Registration failed for type 'counter'. The type name is invalid.

I have this error when using this polyfill. The thing this error appears when using some names when registering them. Is there any restriction on naming of components that I'm not aware? It must be something silly but just in case..
Ex: Registering a component with name 'counter' gives me the error. However, using 'counter-component' is ok.

I'll will push my code later.
Trying to figure out the error for now...

dre-ie8-upfront-fix enumerable error

I'm getting the error 'enumerable' attribute on the property descriptor cannot be set to 'true' on this object when running wrapSetter() on 'textContent' in IE8.

In the max version of the file this is on line 64:

defineProperty(ElementPrototype, name, substitute);

Custom element won't render in nested element under IE8.

Look at the following code, 123456 will be displayed in other browser but IE8,
IE8 only show 123,a-item element does not render. What should I do?

document.registerElement(
    'a-item',
    {
        prototype: Object.create(
            HTMLElement.prototype,
            {
                createdCallback: {value: function () {
                    this.innerText = this.getAttribute('name');
                }}
            }
        )
    }
);

document.registerElement(
    'a-list',
    {
        prototype: Object.create(
            HTMLElement.prototype,
            {
                createdCallback: {value: function () {
                    this.innerHTML = '123<a-item name=456></a-item>';
                }}
            }
        )
    }
);

var a = document.createElement('a-list')
document.body.appendChild(a);

Comparison to -1

I saw a lot of index comparisons like (-1 < index). Is there any case Array#indexOf returns a value lower than -1 or NaN?

Test Fails in FireFox (39.0): constructor (1, 1, 0)

Visited this url: http://webreflection.github.io/document-register-element/test/

Results (line "contructor (1, 1, 0)" is in red):

1 Tests Failed
main (2, 0, 0)
as XDirect constructor (3, 0, 0)
as XIndirect constructor (4, 0, 0)
as innerHTML (4, 0, 0)
as

innerHTML (5, 0, 0)
as createElement(x-direct) (4, 0, 0)
as createElement(div, x-indirect) (5, 0, 0)
attributes (5, 0, 0)
offline (5, 0, 0)
nested (2, 0, 0)
className (5, 0, 0)
registered after (5, 0, 0)
constructor (1, 1, 0)

unknown

simulating a table element (2, 0, 0)
if registered one way, cannot be registered another way (2, 0, 0)
is="type" is a setup for known extends only (2, 0, 0)
innerHTML helper (1, 0, 0)
innerHTML invoke createdCallback via type (1, 0, 0)
innerHTML invoke createdCallback via extends (1, 0, 0)
innerHTML already registered elements still work (4, 0, 0)
innerHTML with nested elements (2, 0, 0)
innerHTML attributes present on createdCallback (1, 0, 0)
innerHTML attributeChangedCallback (1, 0, 0)

Synchronous attributeChangedCallbacks

Hi again,

coming from the same perspective as in my previous pull-request I was wondering how we could improve the polyfill experience of the attributeChangeCallbacks compared to the native experience?
A native attribute change would trigger an event callback in the same run-loop. This would also happen for the polyfill implementation of IE9. However, in Safari or the current Firefox the attribute change event would only be noticed in the next MutationObserver callback.
What about changing the polyfill to always behave synchronous as in the case of IE9? What about making the handling of these changes faster by returning earlier on style attribute changes?

Can i defer registration of an element?

Hi, I love custom elements and this project that allow us to support they even in ie8 :)
I'd like to know if i can write my custom markup first and then register all my custom elements. For instance, loading a <script async> tag at the bottom of the page where my elements will be registered.

Thanks
Marco

Patched createdElement breaks instanceof.

Noticed this one when upgrading from 0.5.4 to 1.0.1.

See this super dummy test case.
Unfortunately, this breaks some third-party code which rely on instanceof HTMLElement checks.

Currently digging into the recent changes to know if this is fixable in some way and hopefully send a PR.

Thanks for your input.

Exclude examples from NPM package

Currently if I install this shim from NPM, it comes with ~108MB of examples, which I would imagine most people don't use. We use this shim a lot at the Seattle Times, and although hard drives are fairly large these days, it does add up. Of course, you've already got a .npmignore file here--could you add examples/* to it, to cut down on the weight and speed up our install times?

detachCallback not triggering during page load

<!DOCTYPE html>
<html>
  <head>
    <title>Modern DOM</title>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0,minimal-ui">
    <!--[if lte IE 9]><script>(function(f){window.setTimeout=f(window.setTimeout);window.setInterval=f(window.setInterval)})(function(f){return function(c,t){var a=[].slice.call(arguments,2);return f(function(){c.apply(this,a)},t)}});</script><![endif]-->
    <script src="//cdnjs.cloudflare.com/ajax/libs/dom4/1.5.2/dom4.max.js"></script>
    <!--[if IE 8]><script src="//cdnjs.cloudflare.com/ajax/libs/ie8/0.2.9/ie8.max.js"></script><![endif]-->
    <!--[if IE 8]><script src="build/dre-ie8-upfront-fix.max.js"></script><![endif]-->
    <script src="build/document-register-element.max.js"></script>
    <!--[if IE 8]>
      <script src="//cdnjs.cloudflare.com/ajax/libs/es5-shim/4.1.0/es5-shim.js"></script>
      <script src="//cdnjs.cloudflare.com/ajax/libs/es5-shim/4.1.0/es5-sham.js"></script>
    <![endif]-->
    <script>
        document.createElement('x-a');
    </script>

  </head>
  <body>
    <x-a id="abc" style="background:red;height:100px;display:block;"/>
<script>
        var MyElement = document.registerElement(
          'x-a',
          {
            prototype: Object.create(
              HTMLElement.prototype, {
              createdCallback: {value: function() {
                console.log('here I am ^_^ ');
              }},
              attachedCallback: {value: function() {
                console.log('live on DOM ;-) ');
              }},
              detachedCallback: {value: function() {
                console.log('leaving the DOM :-( )');
              }},
              attributeChangedCallback: {value: function(
                name, previousValue, value
              ) {
                if (previousValue == null) {
                  console.log(
                    'got a new attribute ', name,
                    ' with value ', value
                  );
                } else if (value == null) {
                  console.log(
                    'somebody removed ', name,
                    ' its value was ', previousValue
                  );
                } else {
                  console.log(
                    name,
                    ' changed from ', previousValue,
                    ' to ', value
                  );
                }
              }}
            })
          }
        );
    </script>
    <script>
    var el2 = document.querySelectorAll('x-a');
    for (var i = 0, len = el2.length; i < len; i++) {
        document.body.removeChild(el2[i]);
    }
    </script>
  </body>
</html>

if no

 <script>
        document.createElement('x-a');
    </script>

in

can't trigger detachedCallback;

when i register a type extension for a exist custom element, must throw error but no.

see http://w3c.github.io/webcomponents/spec/custom/#dfn-definition-construction-algorithm

If NAME was provided and is not null:

  • Let BASE be the element interface for NAME and NAMESPACE
  • If BASE does not exist or is an interface for a custom element, set ERROR to InvalidName and stop.

when i register a type extension for a exist custom element, must throw error (chrome is right because use intern registerElement, but other is wrong because that have no intern registerElement, use document-register-element.js)

var XA = document.registerElement( 'x-a', {
      ....
});

// must throw error
var XD = document.registerElement( 'x-d', {
       'extends': 'x-a' 
});

IE 10 - Problem with the "MutationObserver" workaround

Hi.

I've been having a problem with IE 10 when i create a custom element and, in the postRender, i try to move another custom element inside the first one. Because the "DOMNodeInserted" is triggered and the cycle of the "onDOMNode" begins. The child element starts the createdCallback but when is appended isn't rendered.

For example:

/* This is called inside the cycle of the createdCallback */
postRender: function() {

var contentEl = this.querySelector('.parent-content'),
    child = this.querySelector('.child'),
    item,
    i = 0;

while (child.children.length > 0) {
      item = child.children[0];
      contentEl.appendChild(item);
}

}

The "item" appended is a custom element but it's shown without rendering.
In the browsers that support the "MutationObserver" this works fine.

I've been searching but can't find a solution. Someone else can reproduce it?

Script repeated

In examples/base.html this line:

<script src="//cdnjs.cloudflare.com/ajax/libs/es5-shim/4.1.0/es5-shim.js"></script>

Is repeated twice, character for character. Is that intentional to work around some IE8 weirdness (I spent enough years hacking on IE4+ to believe that) or a bug?

document.registerElement() vs. constructor

I've noticed that under some circumstances (I'm still working on a test case), the polyfill can behave differently depending on whether you create an element via document.registerElement() and the class constructor. Specifically I've noticed that, even though the element is registered, using drE doesn't always instantiate the instance methods or property accessors synchronously, or fire lifecycle events such as attributeChangedCallback. In my case, there are sometimes hundreds of the custom elements being created in the same frame, and if I set their attributes right away, the lifecycle events (including attachedCallback) never fire. Regardless of what the problem was, using the constructor instead of document.registerElement() solved my specific problem.

I peeked at the code and didn't see anything in the shimmed document.createElement() function that suggests it's asynchronous, though. So I'm at a loss for what could be happening here. Are instances of registered elements queued and initialized asynchronously under any circumstances? And if so, what's the best way to know when they're finally ready? I saw some discussion about this in #5, but it didn't seem definitive.

Implement WebComponentsReady event

Hi, and thanks for this excellent polyfill ;)

As @einarq reported (#4), async upgrade of custom elements makes a bit hard to work with, but adding a 'WebComponentsReady' would help.

I'll try to look into the code next week in order to provide a patch if you're OK with that.

ie8 purge() maybe have a bug when detached more than one Element?

function purge() {
  for (var
    node,
    i = 0,
    length = targets.length;
    i < length; i++
  ) {
    node = targets[i];
    if (!documentElement.contains(node)) {
      targets.splice(i, 1);
      verifyAndSetupAndAction(node, DETACHED);
    }
  }
}

if targets.length bigger than 1, when targets.splice(i, 1); targets.length is change, then node = targets[i] will be undefined , so documentElement.contains(node) will throw a error

Usage examples, maybe with customelement

Before all, thanks you for this polyfill, I use this in our commercial app and all simple cases works great! Now, how about to add more complex and realistic examples of usage, with some customelement polyfill (template element) or analog?

`createdCallback` no longer invoked after 0.3.5

Hey @WebReflection,

I went to update your polyfill from 0.3.0 and noticed that my clock face demo no longer works in non-Chrome browsers. I tracked the issue down to the 0.3.5 release (as it works fine with 0.3.4), and it seems to be this commit.

Any idea what's up here? My example works fine in Chrome, and it also works fine if I use Google's polyfill. I might try to dig into the code later, but for now I thought I'd create this as an fyi.

Thanks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.