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

DOM body's first div is used, but could be used by browser extension #339

Closed
sk22 opened this issue Aug 23, 2017 · 7 comments
Closed

DOM body's first div is used, but could be used by browser extension #339

sk22 opened this issue Aug 23, 2017 · 7 comments

Comments

@sk22
Copy link

sk22 commented Aug 23, 2017

Do you want to request a feature or report a bug?
A conflict, so more of a bug

What is the current behavior?

When using a browser extension that alters the DOM, like Emoji One, the Preact app might not be rendered to the correct spot, or even twice. For example, Emoji One injects an invisible element as the first child of body after some time. Since the app appears always to be rendered onto the first child, this can lead to problems.

If the current behavior is a bug, please provide the steps to reproduce.

  1. Install the Emoji Keyboard by Emoji One Chrome extension
  2. Create a new Preact project
  3. Serve it (I'm using serve because I'm having problems with SimpleHTTPServer)
  4. Add some network throttling (e.g. Slow 3G)
  5. Do a hard reload (CTRL + Shift + R) and watch the app getting rendered twice

What is the expected behavior?

The Preact app should explicitly be rendered to one specific element, e.g. determined by ID or reference.

(In case the selector isn't the cause of the problem, my ideas wouldn't help)

Please mention other relevant information.

  • node version v8.0.0
  • Operating system: Windows 10
@developit
Copy link
Member

Interesting - I don't think we can force people to render into a container, but we can definitely have SSR mark the rendered app's root node and give that preference over document.body.firstElementChild. FWIW the offending code is here.

@jojo05
Copy link

jojo05 commented Aug 24, 2017

I thought that when using a template, Preact would render to a specific element. Isn't that the case ?

I think preact-cli init is missing one example using an html file (equivalent to Create-React-App public/index.html).

Also, IMHO, some of the functionality in @zouhir preact-habitat should be part of preact-cli, to make it really easy to use any preact component on any html page

@jojo05
Copy link

jojo05 commented Aug 24, 2017

Why not have public/index.html like with CRA and always render to a given div ?

@reznord
Copy link
Member

reznord commented Aug 24, 2017

Why not have public/index.html like with CRA

We are not exposing the index.html file but you can pass your template to the watch/build commands using preact serve --template "template.html"

In this you can do any sort of modifications which you want to the HTML file 😄

@jojo05
Copy link

jojo05 commented Aug 24, 2017

Yes, I mention template in my first comment, but my second comment is more focused on the current issue, on a specific way to solve it.

@sk22
Copy link
Author

sk22 commented Aug 24, 2017

I guess it works this way because no one expects that the DOM is manipulated by anything other than Preact, so document.body.firstElementChild was simply used. That's no problem, as long as the Preact is the only one who's manipulating the DOM.

Sure, this could probably be fixed by introducing a div with an ID and rendering it to that instead of the body's first child. But, the div itself isn't available in the template itself, but appears to be injected by webpack (not sure about how that works): https://github.com/developit/preact-cli/blob/master/src/resources/template.html#L25-L27

@developit
Copy link
Member

developit commented Aug 31, 2017

I think if we inject a root ID/attribute during SSR (by post-processing the rendered HTML) we can make this nice and transparent.

// during SSR:
let bodyHtml = render( .. );
bodyHtml = bodyHtml.replace(/^(<[a-z][a-z0-9:-]*)/i, '$1 data-preact-root');

... then on the client:

let root = document.querySelector('[data-preact-root]') || document.body.firstElementChild;

render(<App />, root.parentNode || document.body, root);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants