Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Missing <!DOCTYPE html> #91

Closed
jimafisk opened this issue Dec 11, 2020 · 8 comments
Closed

Missing <!DOCTYPE html> #91

jimafisk opened this issue Dec 11, 2020 · 8 comments
Labels
bug Something isn't working

Comments

@jimafisk
Copy link
Member

This should be included with the default starter.

@jimafisk jimafisk added the bug Something isn't working label Dec 11, 2020
@jimafisk
Copy link
Member Author

jimafisk commented Jan 11, 2021

This is a little tricky unfortunately. If you try to simply add <!DOCTYPE> to your layout/global/html.svelte you'll get an error like this in the browser console:

Error in Chrome
DOMException: Failed to execute 'createElement' on 'Document': The tag name provided ('!DOCTYPE') is not a valid name.
    at element (http://localhost:2222/spa/web_modules/svelte/internal/index.js:203:21)
    at claim_element (http://localhost:2222/spa/web_modules/svelte/internal/index.js:342:38)
    at Object.l (http://localhost:2222/spa/global/html.js:105:15)
    at claim_component (http://localhost:2222/spa/web_modules/svelte/internal/index.js:1385:20)
    at Object.l (http://localhost:2222/spa/ejected/router.js:36:4)
    at init (http://localhost:2222/spa/web_modules/svelte/internal/index.js:1469:40)
    at new Component (http://localhost:2222/spa/ejected/router.js:145:3)
    at http://localhost:2222/spa/ejected/main.js:17:3
Error in Firefox
DOMException: String contains an invalid character

In the node_modules/svelte/internal/index.mjs file there is a function claim_element() that returns an element. The standard way this works is it calls function element() which simply does a return document.createElement(name);. Unfortunately this is what throws our error because you can't pass "!DOCTYPE" as name to create an element.

We could create a doctype declaration like this:

document.implementation.createDocumentType('html', '-//W3C//DTD XHTML 1.0 Transitional//EN', 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtdd');

So we could just check if name === '!DOCTYPE' similar to how they check for svg in the return statement of function claim_element() and just run our own function to get a <!DOCTYPE html> element instead.

@jimafisk
Copy link
Member Author

If doing ^ we'll still get errors like:

  • Firefox: Uncaught TypeError: document.implementation.createDocumentType(...).getAttribute is not a function
  • Chrome:
TypeError: node.getAttribute is not a function
    at attr (index.js:269)
    at Object.h (html.js:131)
    at Object.l (html.js:128)
    at claim_component (index.js:1395)
    at Object.l (router.js:36)
    at init (index.js:1479)
    at new Component (router.js:145)
    at main.js:19

From node_modules/svelte/internal/index.mjs line 268:

function attr(node, attribute, value) {             
    if (value == null)
        node.removeAttribute(attribute);
    else if (node.getAttribute(attribute) !== value)
        node.setAttribute(attribute, value);
}

@jimafisk
Copy link
Member Author

You can't run .getAttribute() on <!DOCTYPE> because its prototype is DocumentTypePrototype. A regular element, like a <div> for instance is HTMLDivElementPrototype > HTMLElementPrototype > ElementPrototype where it inherits the getAttribute() method.

test ^ in the console
document.implementation.createDocumentType('html', '-//W3C//DTD XHTML 1.0 Transitional//EN', 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtdd');

vs

document.createElement('div');

We can check that we have an instanceof HTMLElement before trying to do any attribute manipulation to fix those errors. Here would be the full updates:

Add new func around line 231:

function doctype_element(name) {
    return document.implementation.createDocumentType('html', '-//W3C//DTD XHTML 1.0 Transitional//EN', 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtdd');
}

Check that you have an HTMLElement around line 262

function attr(node, attribute, value) {
    if (node instanceof HTMLElement) {
      if (value == null)
          node.removeAttribute(attribute);
      else if (node.getAttribute(attribute) !== value)
          node.setAttribute(attribute, value);        
    }
}

Update line 353:

return svg ? svg_element(name) : name === '!DOCTYPE' ? doctype_element(name) : element(name);

@netaisllc
Copy link

I'll chime in here (uninvited :)).

When adding the doctype tag as you note above in the 2nd comment, you not only throw that error, it also pretty much stuffs up Svelte & the DOM relationship. I couldn't figure out what was blocking some dead simple click events until I decided to NOT add that element. (Before I saw this issue...)

Jim, you might have excellent reasons for the html.svelte and head.svelte components and the attendant approach, but in reading the above it honestly looks like your grinding on a small detail, albeit an important one -- everyone should want that doctype tag.

You know your aims and internals far better than me, but those components - the head component in particular, strike me oddly. I guess I'm influenced by Svelte app development more than SSG/SSR and I've used <svelte:head> for all sorts of stuff before.

Would a reserved file artifact - say, index.template - not work as a place to "can" all the boilerplate like doctype, head and even body? You could then:

  • direct us to use the conventional <svelte:head> to take care of the title and other stuff,
  • force the "main" component to mount to the body property (which is again pretty close to conventional in Svelte land),
  • and not have to have the html and head components at all

Note that you already have "be careful" language around the global html.svelte; that language would just shift over to the index file template, as in "You can add assets and scripts here as needed, but if you change X, Y or Z, you'll break your app".

IDK maybe this is too naive, but I think it would eliminate a possible WTF that we face right out of the box, put the emphasis on userland components (which is what you're driving at), and allow Plenti to easily support any crazy combination of <link> or <script> assets that somebody needs.

With respect...

@jimafisk
Copy link
Member Author

Everyone is invited to chime in! I always appreciate the feedback :). These are really good points @netaisllc, and you're echoing the advice provided by the Svelte core team regarding the related hydration issue: #76. These problems stem from trying to hydrate the full dom instead of setting an entrypoint and hydrating the body or some sub element like you suggest. I'm probably being overly stubborn about this implementation detail, but I'm shooting for a dev experience that feels like a traditional SSG where you only have to worry about content and templates. I'm not sure it's quite there with the current implement either, I'll need to think on this a bit... Thanks for the insights, your approach might ultimately be where we end up.

@jimafisk
Copy link
Member Author

jimafisk added a commit that referenced this issue Jan 26, 2022
@jimafisk
Copy link
Member Author

Temp solution is to inject <!DOCTYPE html> into the HTML during the build and via JS in the router when the page hydrates and the components mount to the dom. I don't love it for the lack of flexibility for the end user to control their doctypes, but it gets us out of quirks mode for now.

@jimafisk
Copy link
Member Author

The temporary fix is incorporated in v0.5.1. You can eject router.svelte if you want to modify the SPA injection (you can't adjust the HTML injection for now, sorry). In the future I'd love to remove this abstraction and make the doctype something you can simply update in your template, but for now this was the easiest way to allow Plenti to continue exposing the full project in simple templates that hydrate themselves to add interactivity, without modifying the svelte internals. So I'm closing for now, but feel free to continue to add thoughts so we can get to a more permanent solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants