Hyperhtml: Ideas from lit-html

Created on 8 Aug 2017  ยท  64Comments  ยท  Source: WebReflection/hyperHTML

While at this stage it's definitively inferior in terms of features, there are few ideas in lit-html that could be borrowed by hyperHTML keeping viperHTML counterpart compatibility a must-have.

Everything discussed in here will result into a major update due inevitable incompatibility with the current implementation but before going that far, I'd like to investigate few things.

Drop Comments ?

Solution: NO. Comments were not just improving performance splitting automatically the layout in chunks, comments were mandatory to solve partial tables layouts. There's no way without comments you can obtain the same. Also, dropping was a way to have attributes without quotes but current V1 candidate already support that.


lit-html is using {{}} as placeholder while hyperHTML is using <!--hyper-->. The reason for hyperHTML to chose comments is to have best performance on template creation too, not only on successive updates.

To be checked:

  • [ ] how badly performance would be affected walking nodes and, per each node, their text content (splitting it in chunks to create new nodes)
  • [x] is {{}} supported without quotes (attributes) on every browser?
  • [x] is {{}} supported with or without quotes (attributes) on svg elements?

Partial Attributes ?

Solution: NO. Partial attributes are YAGNI. You can already have template literals within interpolations so that either through arrays or string concatenation or even functions you can address attributes.

html`<div class=${`foo ${maybeBar ? 'bar' : ''}`}>Foo bar?</div>`;
html`<div class=${'foo' + (maybeBar ? ' bar' : '')}>Foo bar?</div>`;
html`<div class=${['foo', maybeBar ? 'bar' : ''].join(' ')}>Foo bar?</div>`;

html`<div style=${`top:${top}; left:${left};`}>x</div>`;

This has been asked more than once and I still believe it's not needed at all and it makes templates more messy instead of cleaner. However, lit-html supports partial attributes.

To be checked:

  • [ ] how badly performance would be affected walking attributes and, per each node, their text content
  • [ ] how costly is each new attribute composed update
  • [ ] what are the use cases - are use cases so common ?

HTML opt in ? see comment

lit-html is an "always textContent" template thing. It understands it's not text only if the interpolation is a rendered lit thing, hence at distance, post parsing or after the first pass.

hyperHTML has semantics to understand the intent upfront but there are cases where it needs to have an explicit opt-in or opt-out from text.

The recently introduced hyperHTML.escape(text) covers the opt-out from html but there's no official opt in.

hyperHTML.wire could be a good candidate for that, but I'm keen to hear other ideas too.

enhancement question

Most helpful comment

I'm looking at this as a sort of mixin system

mixin is a huge word ... let's say I've just brought possible validation, highlighters, plugins to the library with a single, very simple, hyperHTML.define(name, callback) mechanism.

The callback will receive whatever content you want, even a user object, and decide what to do with the result, which could be any result as in text, Promise, DOM node, or an Array of the same.

It could also return an explicit intent as {text: 'content'}, these are all handled the same.

Update

I've introduced, after @marcoscaceres suggestion, a placeholder builtin mechanism too.
This is basically the equivalent of the previously discussed until.

render`
 <div class="login">
  ${{
    text: fetch('credentials').then(b => b.text()),
    placeholder: 'Loading credentials ...'
  }}
 </div>
`;

A placeholder can be used in conjunction of text, any, html or any defined transformer.

It's value, at the same time can be text or any other hyperHTML value, including Array, DOM nodes, or {html: 'content'} ... really ... same thing all over :smiley:

All 64 comments

lit-html seems to have a nice way of assigning placeholder nodes to represent async partials via until(Promise, defaultContent). It would be cool if something similar could be added to hyperHTML.

hyperHTML.async(...) ?

hyperHTML.async(asyncThing, "Loading..."), looks good to me!

second argument as text-only placeholder? any placeholder?

I think the intent of "until" is more clear.

hyperHTML.until(promise, "Loading...") ?

I forgot viperHTML.async has a meaning already so .until in _hyperHTML_ makes more sense but it cannot possibly be compatible with _viperHTML_ anyway (which is also a bit of a bummer due inability to reuse the same template declaration)

I like the "html opt-in" thing. Simpler to understand than the whitespaces declaration and can be implemented without perf loss.

hyperHTML.wait ?

Simpler to understand than the whitespaces declaration

it's a semantic. > ${'text'} </ VS >${'html'}</ I am not sure why this is seen as difficult ... but ...

can be implemented without perf loss.

Nope. That's the reason hyperHTML went for semantics in the first place: you can understand the intent upfront. lit-html has to parse and also to decide what to do only once the interpolation is resolved so it needs 2 passes VS one.

If anything, performance is not surely a win for lit

So if you have to sacrifice perf for opinable syntax changes, I think they not worth it.

@gdorsi I wonder if you've used _hyperHTML_ before. I don't find its 2.5 rules too difficult to grasp.

Also @gdorsi, for explicit text, you can always use \t (as in _t_ ext)

html`a <a href="#">\t${'b'}</a> c`;

Isn't that explicit enough?

Another thing I'd like to understand is what would be a reasonable compromise for _hyperHTML_ to be developers-friendly.

The current 2.5 rules are just 2.5 but I can see controversial for many.

What would be the best option, considering all features already available ?

// API
hyperHTML
  .bind // should be called `.render` ?
  .wire // should be called `.html` ?
  .adopt
  .until // ???

Specially the .wire() is basically what .html does in _lit-html_.

The main difference is that a wire is weakly related to a generic object while html in lit always create new content if used in the wild.

On the semantic side, the only thing I could think of that makes sense with current _hyperHTML_ implementation is to always be a textContent unless the interpolation content is not text.

I still don't want to lose html injection capabilities because sometimes that's all you need but I wonder if having <div>${'text'}</div> VS <div>${['html']}</div> would make more sense than having spaces around.

Thinking about the future, based on modern JavaScript, I also wonder if having objects could be the key to solve all the issues.

function withText(render, text) {
  return render`<div>${{text}}</div>`;
}

function withHTML(render, html) {
  return render`<div>${{html}}</div>`;
}

Not only this would look even more like a template, but it'll make absolutely not-ambiguous in terms of intent: object with text property is text, and object with html property is html.

The optional, non-text, condition would be any, meaning Array, DOM nodes, Promise or, in case of string content, html which is surely more popular and demanded than just text when you want text.

I know I'm rushing ideas here but I'm also in the process of writing the whole documentation and if there are changes to make and release a V1 first, I'd be happy to do that now.

Thank everyone for extra hints, thoughts, or suggestions.

Recap

function html(render) {
  return render`
    <!-- text by default for strings -->
    <p>Hello ${'World'}</p>
    <!-- text as explicit intent -->
    <p>Hello ${{text: 'World'}}</p>
    <select>
      <!-- any by default for no-strings -->
      ${['a', 'b'].map(v => `<option value="${v}">${v}</option>`)}
    </select>
    <!-- html as explicit intent -->
    <p>Hello ${{html: '<strong>World</strong>'}}</p>
    <!-- any as explicit intent -->
    <p>Hello ${{any: fetch('thing').then(b => b.text())}}</p>
  `;
}

Summary

  • always textContent for strings
  • easy opt-in for HTML via any other kind of interpolation that is not typeof "string"
  • possible explicit opt-in/out via object literal

Breaking

  • spaces won't mean anything anymore
  • that means no backward compatibility in V1.0.0 already

Pros

  • virtual logic is already compatible with this scenario
  • since using comments won't bring in any benefit anymore, attributes can be assigned without quotes (still no partial attributes support planned at this point, it's YAGNI)

Cons

  • parser needs huge refactoring
  • performance will be degraded because virtual logic is inevitably the slowest one to deal with

I didn't find it hard to understand the 2.5 rules, but making them more explicit can be only a plus.

That said, if you want to make this library perf-first, it's ok to sacrifice usability to be more performant. It's only a matter of objectives.

The initial objective of _hyperHTML_ was to bit the DOM at its own operations in a developer friendly way, without bloating the RAM and CPU with unnecessary Virtual-DOM middleware.

I can say, that achievement has been unlocked 6 months ago already.

Today

The goal is to make it as fast, production ready, and developer-friendly as possible.

I've already achieved the first two points, but the current status is clearly struggling to make it a "_just works_" for newcomers.

Everyone failed at reading and understanding those 2.5 constrains, everyone asked once about attributes without quotes and #text VS html.

I blame lack of proper documentation first, but also if I have to train everyone because they are used to think template literals in such different way, it means I need to improve the library side first.

If anything, lit-html confirmed developers prefer verbosity over magic, so that an explicit opt-in on Web development is seen as "_better_" way to go.

Hence my latest proposals that will sacrifice indeed performance a little bit, but I'll find out a way to bit myself on the final implementation, providing a cleaner way for developers to express their intent.

Everything else in lit-html seems quite unnecessary to me, because everything can be already addressed with current hyperHTML in a way or another so ... yeah, anyone can write modules that exports functionalities on top and everyone can render again a promise once it has been resolved.

The only blocker, or better breaking change I see, is the semantic used to define templates.

That's the most important bit, and I think hyperHTML is already superior in terms of interpolation values analysis, but probably not too developer-friendly when it comes to its own 2.5 rules to follow, due performance, and technical choices, constrains.

Having best from every world would be ideal, specially before publishing tons of documents related to the library.

P.S. the object literal could also be the entry point for plugins. text, html, any would be available by default, {yourPluginName: content} could be how you extend in _hyperHTML_.

The more I think about it, the more I like it, specially because it doesn't need global/local scoped variables. ๐ŸŽ‰

A few small points:

If anything, lit-html confirmed developers prefer verbosity over magic

The library didn't confirm anything yet per-se. It's still in its infancy, as recognised by its authors, and there's no need to rush to conclusions. Monitor what lit-html users are reporting, reflect and take some time before making a judgement.

still no partial attributes support planned at this point, it's YAGNI

I disagreed at the beginning, but after having used hyperHTML for a few projects I'm now in agreement that support for partial attributes isn't _required_. It takes a bit of cognitive effort at the beginning, but after a while I learned to carefully manage setting the attributes in one operation.

However, I do believe this should be more prominently featured in the docs.
One of the most common use cases where devs will encounter and trip is this:

<a class="button--save ${isDisabled ? 'button--disabled' : '' }" href="#">

I blame lack of proper documentation first, but also if I have to train everyone because they are used to think template literals in such different way, it means I need to improve the library side first.

Try docs first :)

Try docs first :)

well, I involved you because you were one of those asking to prevent stringification.

Did I convince you that current semantics make sense? :smiley:

@WebReflection just skimmed through this. But my immediate thoughts around partial attributes are that they aren't needed. I feel as though the current pattern is more than sufficient, especially if there's a chance of hyperHTML taking a perf hit just to accomodate html<div class="foo ${maybeBar ? 'bar' : ''}">Foo bar?</div> especially when html<div class="${['foo', maybeBar ? 'bar' : ''].join(' ')}">Foo bar?</div> is perfectly acceptable to my eyes.

With regards to alternate methods of defining text, html, etc I'd be very interested in exploring the option of object passing in the form of { text: "Hello" }, { html: '<div>Hello</div>'}. Having spoken with @marcoscaceres recently the fact is if you as a developer neglect the 2.5 rule with hyperHTML there's a chance of XSS, and I'd be all for and explicit mechanism to declaring textContent vs innerHTML content. Also, correct me if i'm wrong but adding support for object passing would be relatively easy given the current state of hyperHTML, and would enable us to test this new API without having to redo all our existing code.

Finally, I'd like to say thanks for opening this issue and having this discussion around the future of hyperHTML. ๐Ÿ‘

adding support for object passing would be relatively easy

Only if I keep semantics. What you are suggesting is that I keep semantics and I also put objects in the mix.

This is cool but also it's 1/3 of the job because I want to remove semantics completely so that your current templates, if you used html intentionally, will break.

// the following will break
render`<ul>${
  names.map(name => `<li>${name}</li>`)
}</ul>`;

It wouldn't break if you used wires though, but that's another story.

Edit nope, that works. Arrays are treated as explicit html intent.

So, V1 is ready to land:
https://github.com/WebReflection/hyperHTML/blob/V1/hyperhtml.js

Whoever is interested, please test it as much as you can.

List of features and breaking changes here:
https://github.com/WebReflection/hyperHTML/pull/81#issue-249092958

Basically, what I've proposed in here:
https://github.com/WebReflection/hyperHTML/issues/79#issuecomment-320929757

with attributes that won't require quotes anymore.

Interesting enough, not using comments basically breaks every table layout ... if I don't find a way to fix that, I might go back to comments injection and attributes that need quotes.

That would be unfortunate, but the only way to go.

Interesting enough, not using comments basically breaks every table layout

Maybe it is that browsers recognise only <!-- comments --> and the table subtags (td, tr, thead etc) inside tables (in which case you will have no luck with everything else) - or am I misunderstanding something?

yeah, I think lit-html will have fun times with unquoted comments .... ahhahaha, I remember now why I went for comments instead of any other text content.

It breaks very easily.

Current experiments: make it work falling back same way I was falling back for IE, keeping comments in the content to not lose reliability and features with tables.

Stay tuned :smiley:

FYI latest version apparently fixed everything: attributes without quotes and table layout compatibility.

Please give it a try, thank you!

What about everything is a wire by default ? Is this idea worth considering ?

I blame lack of proper documentation first, but also if I have to train everyone because they are used to think template literals in such different way, it means I need to improve the library side first.

For someone that's been using it for a while, I've accidentally screwed this up a bunch of times in bad ways (as you found with the reviews you did for me) - so I'd say it's better to go the explicit route here. Even after having it used it for a while, I still found I was making accidental mistakes because of the way I've become accustomed to writing HTML:

  <p>${like.this}</p>
VS, the following or any whitespace variant 
 <p>
   ${like.this}
  </p>

The possibility of screwing up injecting HTML vs TextNode's also came up as a significant concern in a security review we did in Mozilla when I proposed using the library for the payments project: the concern was not with the library, but with that it's too easy for developers to screw up.

Re: partial attributes - my personal preference is that you don't add that. I personally like that hyperHTML imposes attr="${foo}" - but maybe the developer error messages could be improved for those that get caught out? (don't know if this is hard to detect or not).

Drop Comments ?

I don't have an opinion.

@WebReflection, I'm testing out 1.0 in ReSpec now... will report back soon.

Ok, all seems good ๐Ÿ‘ All my tests pass, and all UI components are rendering as expected.

I'll add that I'm happy to see the unquoted attribute support too. Makes it all feel less XMLy and more HTMLy ๐Ÿ˜€.

@bigopon

What about everything is a wire by default ?

A wire represents the node. When it comes to existent nodes such document.body, and when it comes to Custom Elements such current node or its Shadow DOM, a wire makes no sense.

Wires are cool to compose layouts, but are not suitable as top level components. You can use 99% of wires but you'll inevitably need a place to render them. That "place" needs to be bound.

@joshgillies

html<div class="${['foo', maybeBar ? 'bar' : ''].join(' ')}">Foo bar?</div> is perfectly acceptable to my eyes.

You can have template literals inside interpolations too. The partial attributes can be created in that way.

html`<div class=${`foo ${maybeBar ? 'bar' : ''}`}>Foo bar?</div>`;

I don't find it particularly less readable than the following

html`<div class="foo ${maybeBar ? 'bar' : ''}">Foo bar?</div>`;

but it simplifies a lot attributes assignment and attributes changes. I'm every day more convinced partial attributes is an absolute YAGNI

@marcoscaceres

The possibility of screwing up injecting HTML vs TextNode's also came up as a significant concern in a security review we did in Mozilla when I proposed using the library for the payments project

You should've told me that. Having a big player using hyperHTML would be key to make the library itself more popular and supported.

Anyway, I think I've now addressed this concern. Are you happy with current constrains?

  • if the interpolation is text, it'll be text content
  • if the interpolation is {text: anyValue} it'll forced to be text content even if anyValue is not text
  • if the interpolation is an Array of strings, it'll be used as innerHTML
  • if the interpolation is {html: anyValue} it'll forced to be innerHTML even if anyValue is not text
  • in every other case the interpolation is resolved and handled accordingly to its type ( Promise, Function, DOM node, or an Array of these )

The paranoid version for text is to always use ${{text}} instead of ${'' + text} but both will produce same results.

If anyone has any major concern about above conditions please raise it ASAP.

Thank you!

P.S. yes, attributes won't need quotes. I'm fine tuning all the things around this but so far so good, there are no blockers.

@WebReflection It may make no sense but the usage could be simpler ?

Having the following ability

var article = hyperHTML`
  <article>
    <!-- markup -->
  </article>
`

render into anything user put into the string is quite comfortable, then it's quite simple to do just 1 extra step:

document.getElementById('root').append(article);

In case it's an existing node or shadow root, we can have the current behavior, which is bind(node)

I'm just thinking it maybe is good. Or if it still doesn't make enough sense, just discard this ๐Ÿ‘

You can already obtain that.

function html() { return hyperHTML.wire().apply(null, arguments); }

var article = html`
  <article>
    <!-- markup -->
  </article>
`;

However, you are trashing layout each time. lit-html doesn't have a mechanism to weakly relate objects with layouts. Accordingly, and as you can see, you can obtain the same result and every time that will be a new <article> node but you're missing out hyperHTML features in doing so.

Specially articles, are created through objects with info about articles. Why would you trash every time previously rendered objects? You can if you want, but I wouldn't :smiley:

Also please consider hyperHTML rather like an object or namespace, because using it directly breaks in many ways. I don't think I need to guard it against direct usage more than a use strict directive :wink:

@WebReflection

html`<div class=${`foo ${maybeBar ? 'bar' : ''}`}>Foo bar?</div>`;

this is straight ๐Ÿ”ฅ ! As such I'm still firmly in the camp of partial attributes as not at all required.

@bigopon as illustrated above, that's exactly how hypercomponent works under the hood, and because we're based off ES6 classes, we always pass hyperHTML.wire(this) as a bound wire.

Update

You can define your own transformer now, and return whatever you want (string, DOM node, Array, Promise). Example:

hyperHTML.define('encode', encodeURIComponent);

var entry = 'a b c d';
render`
  The function <code>encodeURIComponent</code>
  transform text ${entry} as ${{encode: entry}}
`;

The idea is to keep templates definition as clean as possible, without needing to pollute the scope in which they exist.

Use cases:

  • external files with shared views between client and server (viperHTML friendly, coming soon)
  • keeps template concerns within the template, not within the scope it runs with
  • templates never throws error in isolation due missing this or that

Thoughts ? I think I like it :smile:

I like it! That seems quite useful.

it's kept very little and very simple for now, but I think it has potentials (validation messages, highlights, etc)

One day maybe transformers could be used for attributes too .. but that seems a bit too much now.

@WebReflection interesting, I think I like it, will have to play around a bit more. Immediately I'm looking at this as a sort of mixin system, would that be correct?

I echo @marcoscaceres' view that even after getting used to it, I still
make mistakes because of misplaced whitespace. I welcome the explicit
definition of interpolation output, HTML, text or any.

Just released a small chrome extension using hyper HTML element. I'll have
a go at upgrading it with the updates you made and report back any
findings.
On Thu, Aug 10, 2017 at 8:06 AM Josh Gillies notifications@github.com
wrote:

@WebReflection https://github.com/webreflection interesting, I think I
like it, will have to play around a bit more. Immediately I'm looking at
this as a sort of mixin system, would that be correct?

โ€”
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/WebReflection/hyperHTML/issues/79#issuecomment-321459660,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAD5m5lIKHtqBOAPn_V-u-JBY6i3VYRoks5sWp3hgaJpZM4OwXYN
.

I'm looking at this as a sort of mixin system

mixin is a huge word ... let's say I've just brought possible validation, highlighters, plugins to the library with a single, very simple, hyperHTML.define(name, callback) mechanism.

The callback will receive whatever content you want, even a user object, and decide what to do with the result, which could be any result as in text, Promise, DOM node, or an Array of the same.

It could also return an explicit intent as {text: 'content'}, these are all handled the same.

Update

I've introduced, after @marcoscaceres suggestion, a placeholder builtin mechanism too.
This is basically the equivalent of the previously discussed until.

render`
 <div class="login">
  ${{
    text: fetch('credentials').then(b => b.text()),
    placeholder: 'Loading credentials ...'
  }}
 </div>
`;

A placeholder can be used in conjunction of text, any, html or any defined transformer.

It's value, at the same time can be text or any other hyperHTML value, including Array, DOM nodes, or {html: 'content'} ... really ... same thing all over :smiley:

๐Ÿ’ฏ for the { html: Promise, placeholder: 'Waiting for HTML' } addition! ๐Ÿ‘Œ

Right ... so, V1 is frozen now (in terms of features).

If you find any issue with its new semantics and features, please shout out loudly, thank you :smiley:

FYI viperHTML is already aligned as well.

I'm updating documentation around and will publish officially both versions ASAP.

Thanks for all the help and tests.

Great work @WebReflection - let us know if there's anything we can do to help. ๐Ÿ‘

Agree. Great work! Probably best thing we can do now is help review dev docs and make sure there are no typos, grammar is good, etc.

@WebReflection please feel free to ping me for review.

@joshgillies, all, would also be great if you can help too ๐Ÿ™‚

IE9 is now 100% code covered and green ... I repeat, IE9 is 100% code covered and green!

screenshot from 2017-08-10 15-02-21

Looking good so far. Definitely a good approach that does not require special tooling (jsx).
Do you have any links to public benchmarks?
Adding it to https://github.com/krausest/js-framework-benchmark might be a good idea (allows you to identify bottlenecks in your own lib).

Do you have any links to public benchmarks?

DBMonster already showed it's at same level of the fastest libraries out there.

viper-news is the fastest HNPWA out there.

Testing V1 locally, it looks like next hyperHTML hasn't lost performance too much.

Performance are a concern when these are really a concern. Today hyperHTML is very fast, and on top of that, is very lightweight.

V1 is going to be dev friendly too, so I ask you where is the benchmark about these 3 points put together behind a 4KB library :wink:

There's a certain lib out there that is only 1k that starts with hyper as well :-) Just sayin.

Anyway, you don't even have to publish the result publicly. You can test your impl agains the benchmark and identify the bottlenecks (if there are any).

Also, when you look at the results, they show raw memory used by the libs.
You'll know how lightweight your lib is (compared to others) when you measure it.

@dyu did you come here friendly or just to speculate?

Show me your results thanks.

let me recap with real-world results, but if anyone wants to waste time on other benchmarks, please do, I'm sure the final result will make @dyu realize he could've helped instead of claiming FUD here.

Came here friendly. Not interested in any exchanges. Peace!

So, I have better things to do than that. I'm testing IE9+ and every other browser including UC.

Micro benchmarks on a thing that doesn't even have clear specs just a long list of how to start the thing isn't exactly my focus right now.

Do you want to help this project? Go ahead and write that test, I'll be more than happy to tell you where eventually optimize it.

Do you want to spread FUD without results? I'm afraid I've already proven myself with real-world tests and results, I'm not willing to waste much time on the rest.

Here, try this with all other frameworks:
https://webreflection.github.io/hyperHTML/test/double-rainbow.html

I just applied hyperHTML v1 in my side-project and it was thankfully just a drop-in replacement. No backwards compatibility issues in my usage. No issues to report so far.

It did feel liberating to use whitespace freely again. ๐Ÿ˜„

Thanks for working on this! ๐Ÿ‘

bind and wire do need to be render and html. I haven't been able to understand "wire" except by ignoring the name and thinking "direct html output string... ish thingy" instead. Just needs that little bit of polish from native English usage to get the API more intuitive.

@web3d0 there is a whole new documentation with hopefully better explanations and live Code Pen examples.

These are just a couple of starting point to know more about the differences:
https://viperhtml.js.org/hyperhtml/documentation/#essentials-1

https://viperhtml.js.org/hyperhtml/documentation/#api-0

Always good to have more documentation. My point is the word "wire" sticks in my brain like a wire, and it is difficult to hang the meanings of what it does on the word "wire". If I shift my brain into the Andrea view, I can see glowing Tron dimensions of writhing HTML strings bridging objects, vibrating with white neon wiring the networks of the Master Computer together. For day to day, perhaps "render" and "html" are more understandable. :)

You render bound content and/or wired content.

The html is the reference to perform such operation.

Wires are connections , like cables , strings , between one or more DOM element to a specific, weakly referenced object.

Neither html nor render, describe this behavior.

If you read both links maybe it'd be more clear.

Also see HyperHTMLElement, which has both html property and a render / update concept.

@web3d0 I tend to agree that the terminology takes a bit to get used to, but tbh as @WebReflection points out, neither html or render adequately describe what's happening with regard to wire and bind. As an example does html as a method seem appropriate to describe some SVG element?

Is there a hyperHTML equivalent to https://github.com/Polymer/lit-element?

It's called HyperHTMLElement

Was this page helpful?
0 / 5 - 0 ratings

Related issues

liming picture liming  ยท  7Comments

WebReflection picture WebReflection  ยท  3Comments

abaksha-sc picture abaksha-sc  ยท  6Comments

davidmerrique picture davidmerrique  ยท  7Comments

shanghaikid picture shanghaikid  ยท  8Comments