On this page

        Namespaced HTML

        Namespacing provides a way to let an element establish its own naming context for descendant elements. This makes it possible to keep IDs scoped to a context other than the document's global scope; thus the ability to write collision-free IDs across a page.

        Namespaced HTML is a document that is structured as a hierarchy of scopes and subscopes.

        OOHTML is being proposed as a native browser technology while currently available through a polyfill. Be sure to check the Polyfill Support section below for the features on this page.


        Namespaces are designated with the namespace Boolean attribute.

        In the code below, the given ID is scoped to the element with the namespace attribute.

        <div namespace>
                <div id="some-id"></div>

        And below is a hierarchy of namespaces and sub namespaces.

        <article id="continents" namespace>
            <section id="europe" namespace>
                <div id="about">About Europe</b></div>
                <div id="countries">Countries in Europe</div>
            <section id="asia" namespace>
                <div id="about">About Asia</b></div>
                <div id="countries">Countries in Asia</div>

        The above gives us a conceptual model of repeating objects, each encapsulating its IDs:

         │   ├⏤about
         │   ├⏤countries

        Namespaced Selectors

        Being able to layout elements in namespaces makes it possible to write collision-free CSS selectors. OOHTML introduces the concept of Namespaced Selectors which are regular CSS expressions written with a path notation.

        Namespaced Selectors use the forward slash / to denote a namespace boundary.

        #continents / #europe / #countries {
            color: darkblue;
        #continents / #asia / div {
            color: orange;

        And this convention can be used with existing DOM Selector APIs.

        let aboutAsia = document.querySelector('#continents / #asia / #about');
        let divsAsia = document.querySelectorAll('#continents / #asia / div');


        Namespaced HTML offers an API for traversing namespaces as object trees. One advantage this gives us is that it minimizes selector-based queries.

        • document.namespace: Object - This readonly property gives the document's namespaced IDs - IDs scoped to the document - as an object.

          let continents = document.namespace.continents; // Returns the "#continents" element in the markup above
        • Element.prototype.namespace: Object - This readonly property gives an element's namespaced IDs - IDs scoped to the element - as an object.

          // Get the "continents" article
          let continents = document.namespace.continents;
          // Access scoped IDs with the new "namespace" DOM property
          let europe = continents.namespace.europe;
          let asia = continents.namespace.asia;
          // And for deeply-nested IDs...
          let aboutAsia = continents.namespace.asia.namespace.about;

        Namespace Observability

        The document.namespace property and the Element.prototype.namespace property are implemented as live objects that can be observed for realtime changes in the namespace tree. Live objects are observed using the Observer API.

        // Obtain the Observer API and use the Observer.observe() method
        Observer.observe(continents.namespace, events => {
            events.forEach(e => {
                console.log(e.type, e.name, e.path, e.value);

        We could as well specify just the path to observe on the function's second parameter.

        Observer.observe(continents.namespace, 'africa', e => {
            // We're now also logging the event's value, that is, the element
            console.log(e.type, e.value);

        With the code above, adding a new ID - africa - to the continents namespace would be reported in the console.

        let section = document.createElement('section');
        section.setAttribute('id', 'africa');

        Removing this element would trigger our observer in the same way.


        To observe changes down the namespace hierarchy, we would set the observer's params.subtree to true.

        Observer.observe(continents.namespace, events => {
            events.forEach(e => {
                console.log(e.type, e.name, e.path/*watch this*/, e.value);
        }, {subtree: true});

        We could as well specify just the path to observe.

        Observer.observe(continents.namespace, ['africa', 'namespace', 'countries'], e => {
            console.log(e.type, e.path, e.value));

        Other possibilities can be found in the Observer API documentation.

        Polyfill Support

        The current OOHTML polyfill implementation has good support for the Namespaced HTML Specification. With the exception of Namespaced Selectors, all aspects of the specification are supported. The polyfill additionally makes it possible to customise the following areas of its implementation of the syntax using the OOHTML META tag:

        • attr.namespace - The namespace keyword attribute. The standard namespace keyword attribute is namespace, but you may use a custom attribute name, where necessary.

              <meta name="oohtml" content="attr.namespace=data-namespace;" />
              <div data-namespace>
                  <div id="id01"></div>
                  <div id="id02"></div>
        • attr.id - The namespaced-ID attribute. The standard namespaced-ID attribute is id, but you may use a custom attribute name, where necessary.

              <meta name="oohtml" content="attr.id=data-id;" />
              <div namespace>
                  <div data-id="id01"></div>
                  <div data-id="id02"></div>
        • api.namespace - The namespace property exposed on the document object and on elements. The standard namespace property is namespace, but you may use a custom property name, where necessary.

              <meta name="oohtml" content="api.namespace=ns;" />
          // Get the "continents" article
          let continents = document.ns.continents;

        Learn more about customization and the OOHTML meta tag here.