Search code examples
javascripthtmltypescriptweb-componentnative-web-component

In-page Linking with Shadow Root and Native Web Components


A very simple example of this problem is the <foot-note> custom web component I've created for my new website, fanaro.io.

Typically, what seems to work for in-page linking is creating and id for the specific element and then use an <a> with href="#id_name". However this doesn't seem to work when the elements are inside the shadowRoot, the URL link still gets appended correctly, but nothing happens. Using document.querySelector("#id_name"); also doesn't seem to work. You can click on any of the <sup> footnotes on any of my articles and then use inspection to check this out.

Is this a limitation of Web Components or am I missing something here? If I wish to do in-page linking, will I have to end up ditching Web Components? Or maybe add custom onClick methods?

The problem has been solved and can be found before commit 4d2ef0d4fb5c8fe56c76d60eb7274c85bae81d94. It's also linked in this issue


Solution

  • It is not a Web Component limitation, it is how shadowDOM works.

    A fragment identifier works by ID references, so they only work in the 'main' DOM, not in shadowDOM (and not in IFRAMEs).

    document.querySelector( ) can't access those IDs in shadowDOM either, because the essence of shadowDOM is to isolate content.

    So you either:

    1. don't use shadowDOM, do you really need shadowDOM features?

      That styling should be a global CSS setting, which is applied if you do not use shadowDOM
    2. register a document click listener from every element with shadowDOM, which checks if a fragment identifier is in its shadowDOM scope
      because a Custom Element can access its host, something an IFRAME can not
      This Event model is the best for decoupled components with shadowDOM
    3. write some more complex code that calls all <foot-note> from the main DOM
       [...document.querySelectorAll("foot-note")]
       .forEach( footnote => footnote.shadowRoot.getElementById(...) )
      

    IMHO none of your Components in your site need shadowDOM; it will make your design easier as you no longer have shadowDOM scoped CSS, everything will be global CSS

    The W3C Web Components Standard has 3 distinct technologies

    • Templates
    • Custom Elements API
    • shadowDOM

    Each can be used without the other!