crysalead-js / dom-layer

Virtual DOM implementation.
MIT License
30 stars 1 forks source link

So, there is no native way to do innerHTML #54

Closed bitinn closed 9 years ago

bitinn commented 9 years ago

If I read #14 correctly, everything will be escaped, if I want to introduce an element that takes static html content or something like inline javascript, I have to roll my own?

bitinn commented 9 years ago

Mostly using dom-layer for my own curiosity, trying to write a module that convert my existing virtual-dom templates into dom-layer templates without changing my existing codes, and I do have inline style and javascript :)

jails commented 9 years ago

Not sure to get what you mean. If you have some inline javascript or style you can simply create them using Tag directly https://jsfiddle.net/oty2unoo/

Makes sense ?

jails commented 9 years ago

Btw why did you left virtual-dom ? And is the project you are working on is an open source project ? just curious ;-)

bitinn commented 9 years ago

I haven't left virtual-dom, just trying out new vdom implementation :)

Maybe I didn't fully understand your Text definition, normally one would expect Text to escape its input, so inline javascript won't work (otherwise xss)

jails commented 9 years ago

The Text node is identical to the virtual dom VText node. So there's no reason to have possible XSS using Text. For example:

new Tag("div", {}, [new Text("<b>hello world</b>")]);

generates the following text content "<b>hello world</b>" (i.e all markups will be interpreted as text and not html)

However, you need to pay attention if you are using the innerHTML property to set a tag content:

new Tag("div", { props: { innerHTML: "<b>hello world</b>"} });

In this case it will render "hello world" in bold. So using innerHTML may open rooms for some XSS exploits.

bitinn commented 9 years ago

@jails Then my question is how does one do innerHTML on server-side? using attrs: { innerHTML: ... }only put them as literal attributes, and as you mentioned props: { innerHTML: ... } is empty. I do need to render inline content (be it html, css, javascript) server-side without being escaped automatically.

bitinn commented 9 years ago

Just to add, I was trying to make my virtual-dom templates compatible with dom-layer, like I did with deku:

https://gist.github.com/bitinn/ca83c32f041f89e4e5ad

But dom-layer Tag and Text api appear to be even more restrictive than deku, so I haven't had a full implementation for it yet.

In longrun though, I think making dom-layer api closer to virtual-dom and deku may help ppl adopt this module, I think it's core support for server-side rendering and progressive enhancement (ie. .attach) may prove to be very useful.

similar discussion on deku https://github.com/dekujs/deku/issues/164

jails commented 9 years ago

Just to clear things up, attributes carry additional informations about HTML elements. And it can be defined through an HTML template using some name="value" pairs. Example: <div class="my-class"></div>. So using attrs: { innerHTML: ... } means <div innerHTML="..."></div> which of course doesn't produce the expected result client side. innerHTML is a property of DOM elements so it only has a meaning in props.

A virtual dom is a way to represent any kind of HTML in a "javascript land" (i.e it's not an abstraction which allows to mix both HTML & virtual nodes to write templates). So if you have some HTML to render you will need to represent it using Tag & Text only (either manually or using an html parser). Btw, it's generaly a job for another libraries like JSX or similar. Personnaly I'm not coding with Tag & Text directly. I'm coding with regular HTML templates and I'm using an html parser at built time which convert all my HTML into a dom-layer compatible syntax.

The other option is to put all static HTML outside the virtual dom representation and only using Tag & Text to represent dynamic HTML parts only. Anyhow you won't be able to use some innerHTML property with virtual-dom or Deku and makes it work server side.

Note sure to understand what you mean by more restrictive. In Deku for example it seems to be impossible to set some DOM element properties through ElementNode only attributes has been considered here. So if almost all can be done through attributes, it still some specific cases where properties are required. In virtual-dom properties are the DOM element properties and properties.attributes are the attributes. So it seems quite generic but what if you need to embed some more data in a virtual node ? Do we need to mix them up with classic DOM element properties ? or adding some other parameters like key or namespace, etc. ? In dom-layer Tag take a config. So properties are in props, attributes in attrs, etc. and furthermore you can add any extra data you want to embed in either in data or using a custom key.

Imo dom-layer is a way more flexible than Deku or virtual-dom so I'm really curious to know what limitation you are facing on with dom-layer you was able to acheive with Deku or virtual-dom

bitinn commented 9 years ago

@jails Limitation: I can't find a way to set innerHTML on server-side using dom-layer.

And from what I read, you are saying it's not possible in dom-layer, at least not natively, that's fine, I just need a confirmation.

I use innerHTML a lot with virtual-dom and deku on server-side, so I have no idea why you believe they can't handle innerHTML property. But that's beside my point.

bitinn commented 9 years ago

PS: by restrictive I mean the writing the hyperscript itself, look at my example https://gist.github.com/bitinn/ca83c32f041f89e4e5ad and you can see virtual-dom and deku are quite expressive in their use of hyperscript, I need not to worry about whether it's a Text node or a Tag node too much.

jails commented 9 years ago

I'm not sure to understand. innerHTML works the same way in dom-layer, virtual-dom & deku https://jsfiddle.net/s5p3zfnh/. How are you using virtual-dom & deku with innerHTML server-side since they don't support server side rendering out of the box ?

dom-layer & virtual-dom shares the same virtual nodes. Tag === VNode and Text === VText. There's no difference. And in the same way you can build a similar hyperscript function layer as virtual-dom do.

I'm using the following one, to speed up spec writing for example.

function h(context, children) {
  var context = context || {};
  var children = children || [];

  if (typeof context === "string") {
    return new Text(context);
  }
  var tagName = (context.tagName || "div").toLowerCase();
  delete context.tagName;

  return new Tag(tagName, context, children.map(function(value, key) {
    if (typeof value === "string") {
      return new Text(value);
    }
    return value;
  }));
}

and you can use it like the following for example:

h({ tagName: "span" }, ["hello"]);

Both document.createElement() && document.createTextNode() are required to build an HTML tree dynamically. So both Tag && Text are required. However it's definitly possible to assume that string values are Text and abstract this assumption in an h() hyperscript function.

Makes more sense ?

bitinn commented 9 years ago

I worked on vdom-to-html for virtual-dom so maybe it's a good reference, it render hyperscript into plain html and we do handle innerHTML property on server-side (it's quite simple actually).

https://github.com/nthtran/vdom-to-html

jails commented 9 years ago

Oh ok, well I see no reason to not allow innerHTML to be rendered as is if the node doesn't have any children defined, fixed in https://github.com/crysalead-js/dom-layer/commit/2bce87a0ed8b2a1ffb93b2f17ab418ba3bb9455a