Hyperhtml: Safari (Mac/iPad/iPhone): hyperHTML.wire() with static template does not guarantee the same generated element

Created on 24 Jan 2019  路  15Comments  路  Source: WebReflection/hyperHTML

Tagged template function is not guaranteed to receive the same instance of a template array on Safari, even if it is used with the same template literal. It causes hyperHTML.wire() to generate a different instance of an HTMLElement.

Due to this issue it is not possible to make a component without adding wrapping element.
Here is the simplest example reproducing this issue.
https://codepen.io/anon/pen/QYwoRy
Run it on Safari and wait for some time. On our devices it usually takes less than minute to see an alert.

It seems that Safari needs the _templateLiteral function fix similar to fix for Firefox<55.

3rd parts issue

Most helpful comment

So I've published it as 0.2.0 'cause even if it's a patch it changes slightly the behavior of the utility.

Whenever I'll find time to update all related projects, I will, meanwhile you can force-use that version.

All 15 comments

I have removed all the unnecessary stuff to reproduce the issue, and I am going to file a bug to WebKit.
https://codepen.io/WebReflection/pen/OdVrqN?editors=0010

This is the most annoying kind of bug ever, 'cause it's impossible to feature detect.

However, are you sure you want/need to retain any node reference at all? The fact nodes are preserved in hyperhtml shouldn't translate into "_feel free to reference these and retain these around_" because that's an implementation/performance detail you shouldn't care about.

In which scenario would this be an issue? I am just trying to understand if it's worth it to penalize Safari perf due random glitches on their GC.

In which scenario would this be an issue?

I use it for creating component without top level wrapper. E.g now:

// button with internal state
import Button from 'button.js'

document.body.appendChild(Button({
  text: 'Save'
})

If result element can be changed, we must create wrapper for every component, and result DOM will be something like:

<body>
  <div>
    <button>Save</button>
  </div>
</body>

Assumption that element should not be changed allow us to remove unnecessary wrapper and receive something like:

<body>
  <button>Save</button>
</body>

could you bind a document fragment instead, as unique holder of the button?

const wm = new WeakMap;
const Button = identity => wm.get(identity) || set(identity);
function set(identity) {
  const fragment = document.createDocumentFragment();
  const render = hyperHTML.bind(fragment);
  wm.set(identity, render);
  return render`<button>${identity.text}</button>`;
}

or something similar ?

sorry, I know I haven't really provided a solution, just a ugly work around.

Let's see what Safari/WebKit folks say about their bug, so I'll figure out what I should do here.

Thank you! I'll try your work around.

could you bind a document fragment instead, as unique holder of the button?

it doesn't work, cause after document.body.appendChild(fragment) all button will be removed from fragment and moved to body. So after Button re-render it will be moved back to fragment and removed from body

it looks like they've closed the bug as duplicated, 'cause lit-html opened the same issue already.

Of course if it's Google complaining they listen and fix so ... good lit-html has the exact same issue.

I don't think I'll do anything for the time being, but I'll keep this open in case I change my mind.

May be add option to force use/ignore _templateLiteral function fix?

For safari ES6 it would be great to always turn it on (ES6 used in our debug environment)
For production we use Closure Compiler with transpilation to ES5. it would be great to always turn it off (now property raw is enumerable, so we have unused overhead)

What about hyperHTML.forceTemplateLiteralFix?

the template literal fix is a third part dependency, nothing affected by random properties in this or that library.

https://github.com/ungap/template-literal#template-literal

So ... this would force Safari to the slow path, which I believe is the simplest/best option for the time being.

https://github.com/ungap/template-literal/pull/3

The ugly sniff/hack will be removed once they fix it in a future Safari version.

Your production code will still use the slow path, but:

  1. better safe than sorry
  2. iOS devices are already the fastest one out there, it won't hurt
  3. this might put more pressure in WebKit/Apple folks to fix this properly, since they care about being slowed down

Please try to use that patch in your env and tell me if everything is fine, thanks.

thank you

@ikabirov don't thank me yet, it's not planned to go out until you confirm it works as expected 馃槄

@WebReflection it seems everything is fine

So I've published it as 0.2.0 'cause even if it's a patch it changes slightly the behavior of the utility.

Whenever I'll find time to update all related projects, I will, meanwhile you can force-use that version.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jaschaio picture jaschaio  路  3Comments

moebiusmania picture moebiusmania  路  6Comments

jaschaio picture jaschaio  路  5Comments

varunkumar picture varunkumar  路  6Comments

BentleyDavis picture BentleyDavis  路  8Comments