GithubHelp home page GithubHelp logo

choojs / nanohtml Goto Github PK

View Code? Open in Web Editor NEW
681.0 21.0 57.0 261 KB

:dragon: HTML template strings for the Browser with support for Server Side Rendering in Node.

License: MIT License

JavaScript 100.00%
dom element frontend

nanohtml's Introduction

nanohtml

npm version build status downloads js-standard-style

HTML template strings for the Browser with support for Server Side Rendering in Node.

Installation

$ npm install nanohtml

Usage

Browser

var html = require('nanohtml')

var el = html`
  <body>
    <h1>Hello planet</h1>
  </body>
`

document.body.appendChild(el)

Node

Node doesn't have a DOM available. So in order to render HTML we use string concatenation instead. This has the fun benefit of being quite efficient, which in turn means it's great for server rendering!

var html = require('nanohtml')

var el = html`
  <body>
    <h1>Hello planet</h1>
  </body>
`

console.log(el.toString())

Node with custom DOM

Modules like jsdom implement (parts of) the DOM in pure JavaScript. If you don't really need the performance of string concatenation, or use nanohtml components that modify the raw DOM, use nanohtml/dom to give nanohtml a custom Document.

var JSDOM = require('jsdom').JSDOM
var nanohtml = require('nanohtml/dom')
var jsdom = new JSDOM()

var html = nanohtml(jsdom.window.document)
var el = html`
  <body>
    <h1>Hello planet</h1>
  </body>
`
el.appendChild(html`<p>A paragraph</p>`)

el.outerHTML === '<body><h1>Hello planet</h1><p>A paragraph</p></body>'

Interpolating unescaped HTML

By default all content inside template strings is escaped. This is great for strings, but not ideal if you want to insert HTML that's been returned from another function (for example: a markdown renderer). Use nanohtml/raw for to interpolate HTML directly.

var raw = require('nanohtml/raw')
var html = require('nanohtml')

var string = '<h1>This a regular string.</h1>'
var el = html`
  <body>
    ${raw(string)}
  </body>
`

document.body.appendChild(el)

Attaching event listeners

var html = require('nanohtml')

var el = html`
  <body>
    <button onclick=${onclick}>
      Click Me
    </button>
  </body>
`

document.body.appendChild(el)

function onclick (e) {
  console.log(`${e.target} was clicked`)
}

Multiple root elements

If you have more than one root element they will be combined with a DocumentFragment.

var html = require('nanohtml')

var el = html`
  <li>Chashu</li>
  <li>Nori</li>
`

document.querySelector('ul').appendChild(el)

Conditional attributes

Use a javascript object to conditionally add HTML attributes.

var html = require('nanohtml')

var customAttr = isFuzzy ? { 'data-hand-feel': 'super-fuzzy' } : {}
var el = html`
  <div ${ customAttr }></div>
`

Static optimizations

Parsing HTML has significant overhead. Being able to parse HTML statically, ahead of time can speed up rendering to be about twice as fast.

Browserify

From the command line

$ browserify -t nanohtml index.js > bundle.js

Programmatically

var browserify = require('browserify')
var nanohtml = require('nanohtml')
var path = require('path')

var b = browserify(path.join(__dirname, 'index.js'))
  .transform(nanohtml)

b.bundle().pipe(process.stdout)

In package.json

{
  "name": "my-app",
  "private": true,
  "browserify": {
    "transform": [
      "nanohtml"
    ]
  },
  "dependencies": {
    "nanohtml": "^1.0.0"
  }
}

Bundling

Webpack

At the time of writing there's no Webpack loader yet. We'd love a contribution!

Babel / Parcel

Add nanohtml to your .babelrc config.

Without options:

{
  "plugins": [
    "nanohtml"
  ]
}

With options:

{
  "plugins": [
    ["nanohtml", {
      "useImport": true
    }]
  ]
}

Options

  • useImport - Set to true to use import statements for injected modules. By default, require is used.
  • appendChildModule - Import path to a module that contains an appendChild function. Defaults to "nanohtml/lib/append-child".

Rollup

Use the @rollup/plugin-commonjs plugin with @rollup/plugin-node-resolve. Explicitly import the browser or server entrypoint in your application. E.g.:

import html from 'nanohtml/lib/browser';

Attributions

Shout out to Shama and Shuhei for their contributions to Bel, yo-yoify and pelo. This module is based on their work, and wouldn't have been possible otherwise!

See Also

License

MIT

nanohtml's People

Contributors

andyogo avatar bcomnes avatar bennlich avatar chromakode avatar gmaclennan avatar goto-bus-stop avatar graforlock avatar greghuc avatar him2him2 avatar hugueschabot avatar izumisy avatar jacobevelyn avatar jondashkyle avatar josephg avatar kemitchell avatar lachenmayer avatar mafintosh avatar mantoni avatar mark1626 avatar mauriciopoppe avatar s3ththompson avatar saschanaz avatar shama avatar stevemao avatar tgfjt avatar toddself avatar tornqvist avatar ungoldman avatar yerkopalma avatar yoshuawuyts avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nanohtml's Issues

Elements inside SVG don’t scale

Thank you for your work in bel and yo-yo 👍

Consider this SVG:

<svg width="140" height="140" viewbox="0 0 70 70">
  <circle cx="35" cy="35" r="30" stroke-width="3" fill="red" />
</svg>

The circle should scale up to fill SVG’s, according to its viewbox, but when the element is created with bel, circle doesn’t scale.

Live example: http://requirebin.com/?gist=32979ca8d3231e3a86ac872260fa3322 — the second element is created manually and scales like it should. In DOM they look identical, must be some SVG behind-the-scenes magic?

onload handler being passed wrong el due to morphdom

Hey folks, I'm crossing my fingers that I'm posting the issue at the right level of the stack here, but I believe I've stumbled upon some unexpected functionality that has me perplexed, and I've attempted to isolate it.

const choo = require('choo')
const html = require('choo/html')

const app = choo()

app.router([
  ['/', HomeView],
  ['/about', AboutView]
])

const tree = app.start()
document.body.appendChild(tree)

function HomeView (state, prev, send) {
  return html`
    <div class="home" onload=${onLoad} onunload=${onUnload}>
      Home
      <a href="/about">About</a>
    </div>
  `

  function onLoad (el) {
    console.log('HomeView loaded', el)
  }
  function onUnload (el) {
    console.log('HomeView unloaded', el)
  }
}

function AboutView (state, prev, send) {
  return html`
    <div class="about">
      About
      <a href="/">Home</a>
    </div>
  `
}

Live demo

Open your console, then click "About," then click "Home." Hover over the elements it logs and you'll see that the final "HomeView loaded" log is passed an el that is not on the screen. In fact the onload handler is still dealing with the original el.

The real trouble, of course, is when you're trying to do something like initialise dragula when the view loads.

What's odd is that the onloadids appear to be correct... not sure what's going wrong. Any ideas?

Network requests on each render

Hi! Thanks for the module.

I have an example here: http://requirebin.com/?gist=f2a99cc27c465ed51eb8159830836ca4. In it, each time I call update and bel creates a new element, a local network request is made for the base64 inlined image, even though it did not change.

Should this be happening? I guess it makes sense, new element is created, bel is fetching it’s content. But that results in a poor performance — when I add a relatively large image, like 3mb, the Chrome browser tab can barely function and the interface becomes unusable.

To reproduce: navigate to http://uppy.io/examples/modal/index.html, click “open modal” and drag two files to the dragdrop area. Open network tab, click “upload”.

What am I doing wrong? Thank you!

Babel yo-yoify independent modules without using browserify?

Hi! Need help with yo-yo/choo build setup. I’ve added yo-yoify to the “browserify bundled” version of my build, but I also have a lib version, where each individual module is transpiled with Babel from ES6, like so: "build:lib": "babel src --source-maps -d lib". How would I add yo-yoify to that? Thanks.

It’s needed, because otherwise this happens https://github.com/shama/bel#note.

Thank you.

Transform to convert template strings to document calls

Unfortunately bel doesn't work with hyperxify but I think we could write a transform that goes even further.

Where it converts:

var title = 'hello'
var element = bel`<div><h1>${title}</h1></div>`

into:

var title = 'hello'
var element = (function (p1) {
  var e0 = document.createElement('div')
  var e1 = document.createElement('h1')
  var t0 = document.createTextNode(p1)
  e1.appendChild(t0)
  e0.appendChild(e1)
  return e0
}(title))

Then we support older browsers and get a significant performance boost.

We could move the hyperx function into it's own library that outputs this stack. In here, it create elements. In the transform, it would create javascript strings.

Server side issue with inline styles

Hi,
I'm trying to use inline styles using bel and also trying the new server-side rendering capability.

When I compile this template

bel`<h1 style="color: red">Hey ${name.toUpperCase()}, <span style="color: blue">This</span> is a card!!!</h1>`

It returns the following string

<h1 style="0:c;1:o;2:l;3:o;4:r;5::;6: ;7:r;8:e;9:d;">Hey JOHN,
    <span style="0:c;1:o;2:l;3:o;4:r;5::;6: ;7:b;8:l;9:u;10:e;">This</span> is a card!!!</h1>

Also if you know how can I use react-like inline styles using objects instead of just strings it will be awesome.

Thanks for this tool!

[idea] pseudo inline styles

An idea for handling CSS:

var bel = require('bel')
var css = {
  position: 'absolute',
  top: 100,
  left: 200
}
var element = bel`<div style=${css}>Hello!</div>`

Which would detect the style key and use dom-css to set the .style[prop] directly on the elements (CSP safe). It will even do vendor prefixing.

Remove `global` dependency?

What is the global dependency used for? Is it just used to get a reference to window.document, or does it help this to work in the Node.js environment?

└─┬ [email protected]
  ├─┬ [email protected]
  │ ├─┬ [email protected]
  │ │ ├─┬ [email protected]
  │ │ │ └── [email protected]
  │ │ └── [email protected]
  │ └─┬ [email protected]
  │   └── [email protected]
  └── [email protected]

Would be nice if we could reduce this tree to:

└─┬ [email protected]
  ├─┬ [email protected]
  │ └─┬ [email protected]
  │   └── [email protected]
  └── [email protected]

seems to ignore html attibutes without assigment

<input disabled><br>

returns

<input><br></input>

but

<input disabled="true"><br>

returns

<input disabled=disabled><br>

Is this intentional? Un-assigned attributes like the first example are valid in HTML5 (I believe actually valid in everything except XHTML)

(ie the following is valid according to https://validator.w3.org

<!doctype html>
<title>test</title>
<form>
<input disabled>
</form>

)

SVG namespace

Error with xmlns attribute.

<svg width="150" height="100" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3 2">
      <rect width="1" height="2" x="0" fill="#008d46" />
 </svg>

see #22

Comments in template strings throw errors

bel`<div>
<!-- this is a comment -->
</div>`

Throws an InvalidCharacterError since you can't create an HTML comment via createElement (but can through createComment).

It would be awesome if bel either ignored comments or created them.

Happy to create a PR for either scenario

Rules around unescaping ascii sequences

I'm trying to include raw html, including &lt;:

If do &lt; it gets double-escaped:

> bel`<div>&lt;</div>`.toString()
'<div>&amp;lt;</div>'

But if I just put a raw < in bel gets super confused:

> bel`<div><</div>`.toString()
'<div>&lt;/div</div>'

This seems to work for my use case:

> bel`<div>${'<raw>'}</div>`.toString()
'<div>&lt;raw&gt;</div>'

.. But I only figured that out by guessing. What are the rules around escaping / unescaped literal HTML strings?

Support && operator-based conditionals in expressions

What do you think of supporting && operator-based conditionals in interpolated expressions?

Current syntax:

html`<input type="checkbox" name="reviewed" ${data.reviewed ? 'checked' : ''} />`

Proposed syntax to support:

html`<input type="checkbox" name="reviewed" ${data.reviewed && 'checked'} />`

The proposed syntax is simply more succinct, and I believe it's popular in the react ecosystem. While the above example doesn't save much text, the : '' gets in the way more when you have multi-line expressions -- it becomes a trailing piece that is often overlooked.

Currently, the proposed syntax works in bel if the first condition is truthy, but if it is falsy, bel renders its falsy value, and you'd end up with <input type="checkbox" name="reviewed" undefined />. I believe the way around this would be for bel (or maybe hyperx?) to not return anything from an interpolated expression that evaluates to undefined or false. I believe this is how JSX does it.

If for some reason you want to render undefined or false, I think it would be reasonable to convert it to a string (since it's a string once it's in the DOM anyway). I think the exception to this would be the value 0 -- it may be unexpected to have to convert 0 to a string just to render it to the DOM. 0 comes in to play when you want to do ${rows.length && 'foo'} vs. ${rows.length > 0 && 'foo'}.

/cc @yoshuawuyts

img.onload shadowed; maybe should be documented

I just want to document a little user story wrt the onload event. I'm new to choo / bel, and wanted to fade in some images after loading. So I added a load event handler to an image, like this:

html`<img onload=${imgLoaded} src=${small} srcset=${sources}></img>`

And then in the handler, at first I was expecting a 'normal' onload event:

const imgLoaded = e => e.target.style.opacity = 1

And when I found that e.target was undefined, I logged out e and discovered that it was actually the image object. "Oh... unwrapped for convenience? OK cool, let's continue..."

const imgLoaded = img => img.style.opacity = 1

And of course, that didn't start the fadein after image load, but immediately. I flailed for awhile. I had no idea what was going on. I searched, I played around, and I spent an embarrassing about of time trying to figure it out, and ended up writing this:

const imgLoaded = img => {
    const wait = () => {
      if (img.complete)
        img.style.opacity = 1
      else
        requestAnimationFrame(wait)
    }
    requestAnimationFrame(wait)
  }

Which gave me the desired behavior, but at unnecessary cost. I was glad it was 'working', and all... but called it a day with this horrible nagging feeling. And then... in the middle of the night, I woke up and realized what was actually going on. (Thanks, subconscious!) So I wrote this:

const imgLoaded = img =>
  console.log(img.onload) // DOH!

And finally, this:

const imgLoaded = img =>
  img.onload = () => // like, for reals
    img.style.opacity = 1

Yay. Let's document this, okay?

Thanks 😄

easier way of using HTML entities

Using innerHTML to insert the odd HTML entity such as &nbsp; as described in the wiki is OK. But when HTML entities are used all over the place, this quickly becomes unwieldy. I wish there was some way to keep using the entities inline.

Is there another reason to escape all text other than prevention of XSS from user entered text? If there isn't, I think the default of escaping everything is wrong and should be changed (if possible).

When writing plain old HTML, people are used their <tags> being interpreted accordingly, just like their &entities;, and the rest used as is (i.e. unescaped). I wish bel could mirror that experience.

<textarea> doesnt always morph

Consider this example:

function textarea (value) {
  return yo`<textarea>${value}</textarea>`
}
var el = textarea('foo')
yo.update(el, textarea('bar'))

foo will still be displayed in the textarea. This is because morphdom compares el.value of textarea nodes and bel sets it by appendChild(document.createTextNode()). The browser takes a little bit to compute the textContent into a value, so if you update immediately after, morphdom doesn't get the new el.value.

Separate libs?

Hey @maxogden! You mentioned breaking into separate libs. Would that look something like this?

bel just creates elements and use morphdom or diffhtml to update:

var bel = require('bel')
var morphdom = require('morphdom')

function render (text) {
  return bel`<div>${text}</div>`
}
var element = render('hello')

// ... later ...

morphdom(element, render('changed'))

@nrn also mentioned this too in #9. That would make bel just return pure HTML elements. It doesn't necessarily encourage data down but one could still choose to follow that pattern with the above setup.

.toString()?

Perhaps it would be cool to include a proper .toString() method - I've found it quite useful when debugging in other packages. Right now it has the following behavior:

const bel = require('bel')
const tree = bel`<div>hello world</div>`
console.log(tree.toString())
// => <div></div>

Thanks!

Similar to shama/base-element#5

Wiki out of date?

The wiki says bel does diffing, but it looks like it only creates elements and morphdom is used to diff and update?

bel is a simple element creator. It returns pure DOM elements but still uses efficient DOM diffing to
update.

thoughts on extensible custom tags

So this may not be in the philosophy of bel, but how/should/can I easily create an extensible module for bel/yoyo (the module I use) that allows for custom tags/props that just render functions.

Here is what I'm thinking:

const Button = function (opts) {
    return bel`<button>my ${opts.name} button</button>`
}

const Col = function (_yield, opts) {
   return bel`<div class="column">${_yield}</div>`
}

const customtagsuse = bel`<div><Button name="Nick" /></div>`

// which is actually just

const customtagsuse = bel`<div>${Button({name: "Nick"})}</div>`

// or

const customtagsuse2 = bel`<div><Col> some cols </Col></div>`

// which is actually

const customtagsuse2 = bel`<div>${Col(" some cols ")}</div>`

Kinda reactish, but all it's doing is regexing the string, and replacing it with the element.

I'm thinking of providing a layer to bel.

so something like:

var extensible = require("bel-custom")
var bel = extensible(require("bel))
bel.customElements = {"Button": Button}; // sort of thing

Any thoughts on design patterns, approaches, potential pitfalls, potential benefits @shama?

shouldComponentUpdate for impure DOM nodes

Can you include leaf nodes inside bel components that don't get blown away on re-renders?

const dataVizBiz = (state, prev, send) => {
  function load(el) {
    d3.select(el)
      .selectAll('div')
      .data([4, 8, 15, 16, 23, 42])
      .enter()
      .append('div')
      .style('height', (d)=> d + 'px')
  }

  return html`
    <div onload=${load}></div>
  `
}

Similar to React's shouldComponentUpdate=${() => false} it would nice to have a way to be able to add an element on first render, but allow it to persist on re-renders without being touched. Would be great if we can pass data in onupdate to modify the DOM too.

<div onload=${load} onupdate=${update} leaf></div>

Here's the complete example I'm trying to get working with Choo.
https://github.com/markbrown4/choo-demo/blob/master/7-friends.js

implicit/explicit whitespace

When working with a more strict layout, whitespace between elements can cause trouble. For example, if I have a couple of spans that need to be flush against each other, there can't be any whitespace between the span tags in the generated HTML. Unfortunately, bel (or hyperx?) preserves the whitespace from the input.

bel`
  <p>
    <span>one</span>
    <span>two</span>
  </p>
`.toString()

generates:

<p>\n    <span>one</span>\n    <span>two</span>\n  </p>

To work around this issue, I'd have to

bel`
  <p>
    <span>one</span><span>two</span>
  </p>
`.toString()

Which isn't ideal, especially when multiple tags are involved or if they have attributes.

I like the way React deals with this issue: https://facebook.github.io/react/blog/2014/02/20/react-v0.9.html#jsx-whitespace

Conditional resolution

Is there any solution of implying if..else logic with templates?

As (as far as I know) we can't put if statement directly in body of ${ }, there is probably a need for dedicated if function e.g. bel.if(condition, onTrue, onFalse).

Is something as that envisioned, or maybe already there's some solution (or convention) coined for that?

Unfortunately wiki is very silent on that very common use case.

[idea] expose the renderer somehow

It would be neat if an end user could switch out the renderer of an element when needed. Such as:

var bel = require('bel')
var div = bel`<div>testing</div>`

Renders a native DOM element but if the user is using React or virtual-dom, they could do:

var vdom = require('virtual-dom')
var bel = require('bel')({
  renderer: vdom.h
})
var div = bel`<div>testing</div>`

Now the div is a VirtualNode. The only problem is ensuring .update() works cleanly with those renderers.

Also maybe this should just be left up to the user:

var div = bel`<div>testing</div>`
var vnode = convertToVNode(div)

Even crazier idea, what if there was a global setting. So window.BEL_RENDERER = vdom.h and then any library using bel would render to virtual nodes instead?

Bug in Firefox "TypeError: access to strict mode ..."

Hi,

I'm getting the following error when trying to add an onload handler to a nested element.

TypeError: access to strict mode caller function is censored

Is this expected behavior? The line triggering this error seems to be https://github.com/shama/bel/blob/master/index.js#L68
Tested on Ubuntu / Firefox 48.0
Seems to work fine on Ubuntu / Google Chrome 52.0.2743.116 (64-bit)

Below is a minimal example reproducing the error.

import bel from 'bel'

const renderObj = ({name, x, y}) => {
    return bel `<g transform="translate(${x}, ${y})" class="task" onload=${e => console.log(e) }>
      <rect width=100 height=100 />
      <text>${name}</text>
    </g>`
}


const objs = [
    {name:'foo', x:100, y:100}
];


const svg = bel`<svg onclick=${onclick}>
    ${objs.map(t => renderObj(t))}
</svg>`

document.appendChild(svg);

Kind regards,
Melle

Remove the ids

When a parent renders, the context to a child element is lost. So when that child element needs to .update(), it must re-find itself in the DOM. This is why we currently require an id attribute.

It shouldn't be necessary though. There might be a way with morphdom or diffhtml to know when a child we previously created has been changed then reconnect that element on .update(). Or use another method to discover when a child has lost it's way that doesn't involve attributes and DOM lookups.

At the very least, we can detect whether or not a child requires a lookup if (element.parentNode == null) before falling to the current system.

export createElement

bel is a super neat idea!

Is there a way to export the function that is passed into hyperx so that it can be used with other libraries in the ecosystem that work with createElement functions?

Invalid rendering with missing close

First I thought it was a bug in hyperx, but virtual-dom actually throw, so that leads me to bel being the issue.

The following code renders incorrectly (as I said, virtual-dom throws an error):

bel`<div><p>Hello World<p></div>`.toString() // => '<div>pHello World<p></p></div>'

Notice the p prepended to Hello World.

I'll leave this here until I need to fix it, unless someone gets to it before me 😅

Event for when element is inserted into the DOM

I find myself doing this pattern quite often so knowing when an element has been inserted into the DOM would be useful:

var div = bel`<div>testing</div>`
// ... later ...
div.update(bel`<div>changed</div>`, function (element) {
  // element is div and in the dom
})

Rendering issues when used with ES6 / prototypes

Hey! Using yo-yo in a project with ES6 classes: we have Core and Plugins, and each plugin is inheriting from a Plugin class, which serves a boilerplate. Using super and extend keywords, which is relevant, because the issue goes away without those. Everything gets transpiled down to ES5 with Babel.

Sometimes yo-yo won’t render an element that was created in a constructor with this, like so:

import yo from 'yo-yo'

export class Smth extends Plugin {
  constructor (core, opts) {
    super(core, opts)
    this.strange = yo`<h1>this is strange 1</h1>`
  }

  render (state) {
    const bla = yo`<h2>this is strange 2</h2>`
    return yo`
      <div class="wow-this-works">
        <input type="text" value="hello">
        ${this.strange}
        ${bla}
      </div>
    `
  }

  install () {
    this.getTarget(this.opts.target, this, this.render)
  }
}

<h1>this is strange 1</h1> gets rendered while <h2>this is strange 2</h2> does not. If you move this.strange from the constructor to render function, it works. If you console.log the elements, they always show up in the console. Also, it works if you wrap yo call in a function, like this.strange = function () { return yo'<h1>this is strange 1</h1>' }

And I would think it’s totally this and class issues, except — wait for it — when you replace h2 with h1, it works. Different elements won’t render, but if two of the same are used — it works.

Here is a diff of two transpiled examples: https://www.diffchecker.com/ngpc5whl (the one on the left works, the one on the right does not), and are screenshots:

screen shot 2016-05-31 at 17 01 47

screen shot 2016-05-31 at 17 02 22

I thought maybe someone who wrote bel and knows internals will understand what’s going on here. Where should we even look? Sorry for complicated explanation and thanks in advance.

How to handle text with html tags

For example, from an api I might receive some text:

{ story: '<p>Some story</p>' }

When I put that through bel (actually choo.view):

bel`<div>${story}</div>`

I get:

<div><p>Some story</p></div>

But the <p></p> is just plain text, and not a dom element. I feel like others must have run across this and there's something obvious I'm missing.

I created a requirebin to demonstrate: http://requirebin.com/?gist=d3ac1f22ff3acbd63a1113ddcf560583

code improvements

  • use a Set for SVG_TAGS
  • avoid delete
  • don't overwrite variables (for (var p in ..) { ... p = somethingElse

spread props

say I have a bunch of props to spread like in jsx, how can I achieve that? The following doesn't work

const props = `x=${x} y=${y}`
return bel`<div ${props} />`

update(old, new)

Any thoughts on trasitioning update to being a function that takes the old element as it's first argument, instead of adding a property to the element? It feels like it would fit better with my expectations approaching the interface. And it seems like you could then update elements not created by bel, which would be nice.

Issue on hosting bundled JavaScript code on gh-pages or surge.sh

Hi I'm sorry this issue might not be a bug of bel but I got a issue while trying to host a static page to gh-pages.

I'm pushing the bundled code to gh-pages branch with a npm module called gh-pages. The code looks fine on the gh-pages branch of the repo, but while visiting the gh-page url, I got the following error from Chrome devtools

screen shot 2016-03-13 at 9 48 53 pm

I did a few debug and find this line on gh-pages branch is https://github.com/fraserxu/videos/blob/gh-pages/bundle.js#L44

var SVGNS = 'http://www.w3.org/2000/svg'
var BOOL_PROPS = {
  autofocus: 1,
  checked: 1,
  defaultchecked: 1,
  disabled: 1,
  formnovalidate: 1,
  indeterminate: 1,
  readonly: 1,
  required: 1,
  willvalidate: 1
}

but when opening the page and checking from Chrome debug tools the script becomes to

var SVGNS = 'http:var BOOL_PROPS={autofocus:1,checked:1,defaultchecked:1,disabled:1,formnovalidate:1,indeterminate:1,readonly:1,required:1,willvalidate:1}

I have no idea why is breaking on gh-pages, the code works perfectly fine on my local though.

I also tried to push the same code to a tool called surge.sh but the code is the same as on gh-pages.

Sorry again that this is not really a bug with bel but you may have any idea with this problem?

Wrong attribute parsing

I'm having problems with bel. I try to use bel to render some markdown content with the help of marked library. With this code

const html = require('yo-yo')
const marked = require('marked')

const post = require('./posts/post.md')
console.log(marked(post))
document.body.appendChild(html(marked(post)))

And this markdown content

# hello world

I'm using stringify to require the .md file content. When I console log the marked(post) call, I get

<h1 id="hello-world">hello world</h1>

That's pretty valid html to me, but bel complains with this error.

Uncaught DOMException: Failed to execute 'setAttribute' on 'Element': 'hello-world"' is not a valid attribute name.
at belCreateElement (http://playground-yerkopalma.c9users.io:8080/bundle.js:1447:14)
at http://playground-yerkopalma.c9users.io:8080/bundle.js:1807:12
at http://playground-yerkopalma.c9users.io:8080/bundle.js:1569:45
at Object.1../posts/post.md (http://playground-yerkopalma.c9users.io:8080/bundle.js:7:27)
at s (http://playground-yerkopalma.c9users.io:8080/bundle.js:1:254)
at e (http://playground-yerkopalma.c9users.io:8080/bundle.js:1:425)
at http://playground-yerkopalma.c9users.io:8080/bundle.js:1:443

And the problem is that when the belCreateElement function is called, it is called with wrong attributes

error

Now why is that happening? I thought about an hyperx error, but tried it with hyperscript and it render correctly. Not sure what could it be, also looked at the hyperscript-attribute-to-property library inside hyperx, but again why would it work with hyperscript, but not with bel?

Any help would be appreciated.

Support for virtual-dom style object

Is it reasonable to expect the same kind of support for style objects as in hyperx? I'd like to be able to do the following but this seems to only work with hyperx & virtual-dom/h at the moment.

var bel = require('bel')
var style = { color: 'red' }
var el = bel`<div style=${style}>`
console.log(el)
// <div style="[object Object]"></div>

see also choojs/hyperx#3

[feature] add thunking API

In yo-yo and choo it's a best practice to have element functions in an
application return the same element given the same input. This mechanism is
commonly referred to as thunking.

How would we feel about adding a light wrapper that exposes a thunk function
so that elements can be thunked with little effort. I was thinking of an API
along these lines:

// elements/my-button.js
const thunk = require('bel/thunk')
const html = require('bel')

module.exports = thunk(createMyButton)

// create a cool button that has a different color
function createMyButton (color) {
  return html`
    <button style="background-color: ${color}">
      Click me! :D
    </button>
  `
}

In a choo app this could then be consumed as:

const html = require('choo/html')
const choo = require('choo')

const myButton = require('./elements/my-button')

const app = choo()
app.router([ '/', myView ])

app.model({
  state: { color: 'blue' }
})

const tree = app.start()
document.body.appendChild(tree)

function myView (state, prev, send) {
  return html`
    <main>
      ${myButton(state.color)}
    </main>
  `
}

What do you think? Would this be a useful addition to bel, or should we
continue to point people to use an external thing / write it themselves? I was
thinking of including this into choo, but given that we strongly encourage
people to use bel for standalone components (yay, reusability) I figured it
might make more sense to include it here first, and then have it propagate
upwards through yo-yo to choo. Thanks!

See Also

Move appendChild and attribute logic into files here

Currently the same logic exists across yo-yoify and bel. It would be nice if it could be more shared and driven by this library.

It also means an element created with a specific version of bel may not be compatible with a specific version of yo-yoify. So yo-yoify should really require('bel/appendChild') and require('bel/attrs') from the local element it is transforming to ensure compatibility.

Using JSX with Bel

I was wondering if its possible to use JSX instead of hyperx to create elements with Bel. I like to use Bel and Yo-yo, but sometimes its convenient to go JSX route, because then components could be re-used between Yo-yo/Choo and React projects.

I tried to set jsx pragma to h and then use it like so:

var h = require('bel').createElement

var el = <h1 class="a">Hi</h1>
document.body.appendChild(el)

But children (Hi) never appear, and if you omit class, bel complaints: Cannot read property 'namespace' of null. Got confused, probably missing something.

<label> around an input field eats following elements

I'm not sure what the problem is, but if I have an input field inside a label as recommended by mdn:

bel(`<div>
  <label> <input type="checkbox" checked> text </label>
  <a>outside</a>
</div>`).toString();

// ->
<div>
  <label> <input type="text" type="type" checkbox"="checkbox&quot;" />
    <a>outside</a>
  </label>
</div>

... The following elements somehow teleport inside the label.

[Edited: Added checked to the input tag, which is required to reproduce the bug.]

Using with shadow-dom doesn't trigger onload function

import bel from 'bel';

export default class MyApp extends HTMLElement {
  createdCallback() {
    let greeting = this.getAttribute('greeting');
    let html = bel`
      <div>
        <div id="quotes">
          <div class="header" onload=${() => console.log('test')}>
            ${greeting ? greeting : 'Hello!'}
            <content select="my-name"></content>
          </div>
          <div class="body">
            <content select="my-profile"></content>
          </div>
        </div>
      </div>
    `;

    this.createShadowRoot().appendChild(html);
  }
}

Which I use:

<my-app greeting='Hi'>
  <my-name>Hello</my-name>
  <my-profile>
    This is my profile
  </my-profile>
</my-app>

The DOM renders fine, but on load there is nothing in the console.

bel/on-load: allow for multiple onload/unload events?

Hi there,

This is more of a question than a bug, and affects bel and on-load together.

I'm writing a tiny framework where:

  • views are generated with yo/bel
  • the framework implements "view load/unload" hooks with an on-load call on the generated view

The problem I'm having is that the framework's on-load call will override any prior onload/onunload attributes set within the yo/bel view:

//View supplied to framework
view = yo`<div onload=${c('view load')} onunload=${c('view unload')}>page1</div>`;

//Frameword sets up view load/unload hooks
onLoad(view, c('framework load'), c('framework unload'));

function c (value) {
    return function() {
        console.log(value);
    }
}

document.body.appendChild(view)

//Output is 'framework load', not 'view load'

Any advice on how to handle this? Ideally, I'd like all onload/onunload functions (supplied by both view and framework) to fire, order irrelevant.

I dug through the bel and on-load code and discovered:

  • bel: when creating the view in DOM form, bel makes the appropriate on-load calls for any onload/onunload attributes specified. But there isn't any easy way to get access to the onload/onunload functions that were added (e.g. to wrap/call them from the framework)
  • on-load: I believe only one onload/onunload function-pair can be associated with a DOM element. I wondered if I could associate multiple pairs by running multiple instances of on-load, each with their own data-onloadid attribute. However this is not possible, as require('on-load') would point to the same library.

This hasn't been an issue for the choo framework so far, as I believe a choo view is expected to handle onload/onunload behaviour, and not the choo framework (so not both).

Any advice appreciated :-)

Greg

Injecting strings with HTML content

Is it possible to inject raw a HTML string into a bel element? Right now I can't seem to get that working.

Example:

var bel = require('bel')
var html = '<div>hi</div>'
var body = bel`<div>${html}</div>`

document.body.appendChild(body)

try {
  document.body.appendChild(bel`${html}`)
} catch (e) {
  document.body.appendChild(bel`<div>${e.message}</div>`)
}

Output:

<div>&lt;div&gt;hi&lt;/div&gt;</div>
<div>Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.</div>

http://requirebin.com/?gist=b7081814ea6465115d84


I'm trying to get this working for friends (trying out a rewrite of the front-end using yo-yo). Old method was to convert the html to a vdom element but that doesn't apply in this instance since ain't no vdom around. Here's the part that's causing the issue in friends if you're curious: https://github.com/moose-team/friends/blob/master/lib/elements/messages.js#L44-L58

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.