Fullscreen Elm App in 0.19 (childNode issue) Reopened


#1

Continuing the discussion from JavaScript Exception: Cannot read property childNodes of undefined, with extension Dark Reader:

I am reopening this as I believe it is a big issue for real world Elm applications.

At Humio are have big problems with browser extensions and third-party trackers and other scripts in our Elm application.

We finally after several weeks of hard work were able to convert from 0.18 -> 0.19. We use a fullscreen elm application to be able to control navigation etc. We deployed and people starting complaining about nothing showing up in the browser! Oh no!

Turns out that these people had browser extensions that injected things into the tag of every page (Yikes). But that is unfortunately “the real world” where people have crap like that. Adding anything not controlled by Elm to the body will make Elm throw errors over and over - which is fair.

Unfortunately this completely stops the application from running, and means you cannot make a website based on Elm fullscreen - it simply might not show for many users :S

It also means that you cannot use some tools like Hubspot or Drift for forms and analytics, because you often have no control over where they inject their iframe tag (meaning the end body usually). (We had to remove Hubspot)

A solution is to use an embedded Elm application, but that limits navigation in your elm app, which is a huge sacrifice to make. The best solution in my mind, would be that Elm fullscreen applications could be added to a top level wrapper element in body, and Elm would ignore anything outside that.

I think this merits an option to run Elm fullscreen in a wrapper element. Let us jointly make a RFC and create an issue for this. I know @Aaron_White had a workaround (very hacky) that could be employed for now.


Our Experience upgrading to 0.19 at Humio
#2

Related report: Browser.application doesn't work with popular Analytics approaches

And for those interested in the hack, we did this in our gulp compilation pipeline:

// Monkey patch step to work around Elm's Browser.application deficiencies.
// Please see VENDORHACKS.md for more details
gulp.task("build:scripts:elm:monkey-patch", () => {
  const originalApplicationBodyLoadingCode = "var bodyNode = _VirtualDom_doc.body;"
  const isolatedApplicationBodyLoadingCode = 'var bodyNode = _VirtualDom_doc.getElementById("elmIsolationContainer");'

  return gulp
    .src(output)
    .pipe($.replace(originalApplicationBodyLoadingCode, isolatedApplicationBodyLoadingCode))
    .pipe(gulp.dest(outputPath))
})

#3

I believe that the browser extensions issue was quite marginal in 0.18 but might become more serious in 0.19 due to new way of handling <body> by Browser.application.

A first step could be to gather some real-world numbers. So if there are some companies that use 0.19 in production and have some statistics about runtime errors due to browser extensions in 0.18 vs 0.19 (I know this is not easy), maybe it could help to provide those here?


#4

I see your point. But regardless of numbers, I don’t want my application to die because of a browser extension. We are a B2B and customers can easily have company mandated extensions installed that they have no control over. Browser extensions are not going away, and will no start behaving well either. Elm should have a way to deal with that, at least an opt-in. Injection in the body is common practice for a slew of tools like Intercom, Drift and Hubspot as well. Which means Elm cannot be used with some of those tools. That does not seem right.


#5

We also noticed this problem when upgrading to 0.19. Our solution was to do history updates through ports and go back to Browser.element.


#6

That is absolutely an option, but if you have to do this for any real world application written in Elm, doesn’t it defeat the purpose of having the fullscreen app option at all? We should not have to “work-around” the user’s browser - It is a factor that is out of our control. I would much rather use the new navigation API as intended, and come up with a solution that will make fullscreen work in its target environment.


#7

I 100% agree with you.


#8

As far as I know, rendering a React app directly in body produces the following warning that cannot be disabled:

Warning: Rendering components directly into document.body is discouraged, since its children are often manipulated by third-party scripts and browser extensions. This may lead to subtle reconciliation issues. Try rendering into a container element created for your app.

Given the number of apps using react, this tells something about the risk of doing it.

For now, answers here or on slack seem to indicate that companies with 0.19 either use Browser.element with a javascript port for navigation, or a hack to move the app in a div under body.


#9

Another common source of stuff in <body>: Many sites have a little chat widget in the bottom-right corner. Those are usually made by pasting a third-party script tag into your HTML, which eventually injects the chat widget in <body>.


#10

Hi Simon - yes you are right. I already mentioned that in the original post. HubSpot, Intercom, Drift and similar.


#11

Like this, right?


#12

Yea, but we also added support for replaceUrl.

One important thing that is omitted from that example is to add the url to the href attribute on the anchor tag as well. This will enable enable search engines to crawl the site.


#13

I hadn’t seen that elm/browser/blob/master/notes/navigation-in-elements.md note until now. It sounds a reasonable enough argument, but does that position not mean that Browser.application and consequently all of Browser.Navigation are essentially useless? As any real-world application will have to deal with these browser extensions.

I’d go with Aaron White’s hack and push the Elm element in one.

A potential fix for Browser.application could be to allow the id and presence of a div to contain the Elm node to be passed as optional arguments.


#14

Reading the comments here and on slack I wonder if the default for Browser.application should be to require an element to be passed, with a big fat warning similar to react if body is passed in.

This is going to be a pain point for every new developer writing something for an audience bigger than themselves, with the worst case scenario being that only their end users see problems and not the dev themselves, leading to lots of confusion. Shipping such an unsafe default behaviour doesn’t seem like a very good DX.


#15

From the notes in Browser.Navigation it seems it’s assumed that if you’re using Browser.element is because your using other scripts in the same document. At this point we already know that’s not necessarily the case. I’d wish the core developers with chime in this topic and explain if there are other impediments. If not, then just enable the functionality (with a warning if they want).


#16

Or if no element is passed in, let elm create a container element and append it to the body and take over this element. This way there are mostly no conflict with plugins and third party libraries and the current API wouldn’t have to change.


#17

Before suggesting fixes based on whatever, I think there is information to gather:

  1. Make a list of the browser extensions that are known to cause problems.
  2. Figure out how they are they modifying the <body> exactly. Adding at top? Adding at bottom? Adding indiscriminately somewhere in the DOM?
  3. Sort the list of extensions by how they work.
  4. Figure out how many people use these extensions, so we can weigh the importance in practice. Maybe certain categories of problem only show up in weird ones, but one category is super common and reasonable to work around in virtual-dom.

From there, it will be much easier to find a fix that makes sense for the actual reality of the situation.

Can folks in this thread work on gathering this information and come back with the lessons?


#18

Not exactly what you asked, but an interesting data point, I went dumpster-diving in React’s closed PRs + issues to see if they had any information to back up their warning implementation. The warning came here https://github.com/facebook/react/issues/3207 and appears to be based on the experience with Google Font Loader as documented here https://twitter.com/dan_abramov/status/568566795004723201

Unless you hand write your CSS then you have an extra level of nesting to think about, that just magically appears. Personally I’d prefer a breaking API change that the compiler would catch than a surprise extra element.


#19

A couple more data points…

Vue doesn’t allow/recommend binding to body with the same general explanation of “some extensions” without providing exact information https://vuejsdevelopers.com/2017/05/01/vue-js-cant-help-head-body/

Ember.js injects a wrapper div.


#20

For some additional context:

  • I think adding things at the end of the <body> should be fine as is.
  • I think it is possible to add some logic in virtual-dom to make it safe to add things at the beginning of <body> as well. Not 100% certain, but there is hope.

So if the extensions are all in these two categories, we may be able to just have things work out.

If the extensions are adding things in the middle of stuff, I’m not sure why they’d be doing it in the <body> any more than they’d be doing it in a random <div> somewhere.

So the point of gathering information is that we can figure out if there is a path that just sorts everything out without relying on a “this is not recommended” in the dev console.


If you want to help with this, just do it. Don’t wait for others to do it. Don’t wait for someone to start a “collab” where someone else does it. Just start installing common extensions and see if you can make things mess up with a Browser.application program that changes the number of root nodes on some timer or something.

If you do this, start a new thread where you share the test program and the list of extensions you tried. If multiple people do this, great, we have independent exploration and verification. Much stronger result.