Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
RE:DOM – Tiny DOM library (redom.js.org)
112 points by pkstn on Aug 28, 2016 | hide | past | favorite | 78 comments


Honest question, how is:

  el.email = input(props({ type: 'email' }))
better or easier than:

  <input type="email">
I think the example needs to be more compelling.


I assume you mean to compare it to:

    el.email = (function(){
        var input = document.createElement('input');
        input.type = "email";
        return input;
    })();
...or something similar. Your could also use createContextualFragement or a little function that uses innerHTML, both of which parse a string into HTML (which is problematic). Or you could use a templating library, of which there are many and this is one.


    el.email = document.createElement('input');
    el.email.type = "email";


    el.email = Object.assign(document.createElement('input'),{type:'email'});


el.email = { document.createElement('input'), ...{ type: 'email' }}


I know about the spread operator, but I'm pretty sure that's invalid. `var foo = { document.createElement('input') };` doesn't work, spread or no spread after it.


Ow man.. I saw the Object.assign example and thought i don't like it because it requires an extra polyfill. I can just use a spread and let babel handle it.

If anything it should be `var email = {...document.createElement('input'), type: 'email'}`

But that would result in `Object.assign({}, document.createElement('input'), { type: 'email' })`

Got 4 up votes though..


What supports this feature and how is it implemented?



(Too long to edit my other reply)

Also used was object destructuring: http://odetocode.com/blogs/scott/archive/2014/09/11/features...


$('<input type="email">')


I recently had the pleasure of tracking down the source code for how this works. There are a few methods, but the main one is at least a hundred lines long.


Why did you wrap that in a closure / immediately invoked function expression?


It's a way of hiding local variables. Old "var" variables are function scoped, so you can't even hide them in if-blocks or for-blocks, which makes scope leakage even worse.

If you're a JS programmer you should definitely read Crockford's famous JS book, it explains a lot of JS patterns and the rationale behind them.


Why compare it to that? There is no reason to avoid innerHTML anymore - it's not slow at all.

There is rarely a reason to build HTML using createElement, it's only sometimes needed. The majority of the time just write HTML - it's much easier.

> Or you could use a templating library

Or just use the built in <template> tag.


Not slow compared to what? Creating elements with the DOM API is faster than innerHTML roughly by a factor of 4 on Chrome Canary last I benchmarked it, DOM mutations with bindings are even faster. There is a reason why React switched from innerHTML to generating DOM.


>Creating elements with the DOM API is faster than innerHTML roughly by a factor of 4 on Chrome Canary last I benchmarked it

yes, almost by a factor of 4 :D

check out "render" test cases:

https://cdn.rawgit.com/localvoid/dd96890b82f1d1268df34330b14...


Template libraries are fine, but there are cases where you hope you could just write JavaScript..


When you build large applications, things are not always that simple.. If you want to be able to init the app and then start to update it with minimal work and maximum performance, HTML itself is not enough.


Using crel as a comparison:

var input = crel('input', {type:'email'});

vs:

var wrapper = document.createElement('div'); wrapper.innerHTML = '<input type="email">'; var input = wrapper.children[0];


angle brackets ,we decided long time ago we hate those :)


No we didn't, considering a lot of us write JSX at our jobs.


Both can be true - they certainly are in my case.


In general case, (for all other props), please don't forget to escape a quote in you prop's values.


Let's see: website consists in its entirety of two not-very-illuminating code samples. The documentation on github consists of the phrase "Documentation is a bit lacking yet, please check out the source for now."

I feel like at this point, when there are so many, so so many, javascript frameworks to choose from, if you're going to build yet another one you need to take the time to explain why it exists. What are the advantages of this approach over the many existing alternatives? What problem does it solve? What does it do that others don't? What motivated its creation? Why should someone use this instead of riot or vue or knockout or transparency or $dom or snack or xui or or domtastic or crel or DOMinate or shaven or scriber or zepto or react or jquery or good old vanilla?

Maybe I'm just getting old and cranky (ok definitely I am that) but if someone can't be bothered to document their own code and expects others to dig through the source to figure out what it's for and how to use it, my default assumption is that the code itself is going to be similarly slipshod.


To be honest, I just wanted feedback about the experimental approach.. It's only version 0.0.x


I've never seen this before:

    children(el => [
      el.email = input(props({ type: 'email' })),
      el.pass = input(props({ type: 'pass' })),
      el.submit = button(text('Sign in'))
    ])
Can someone explains what this does and why it is used here? As i understand it this function both modifies the `el` object (whatever that is) and returns an array containing the input elements, but why would you want to do both those things at the same time?


Here’s what I believe will be an accurate translation of that example to the normal DOM API (without having actually looked at the RE:DOM source code):

    const login = document.createElement('form');
    
    login.email = document.createElement('input');
    login.email.type = 'email';
    
    login.pass = document.createElement('input');
    login.pass.type = 'pass';
    
    login.submit = document.createElement('button');
    login.submit.textContent = 'Sign in';
    
    login.appendChild(login.email);
    login.appendChild(login.pass);
    login.appendChild(login.submit);
    
    login.onsubmit = function(event) {
        event.preventDefault();
        console.log(this.email.value, this.pass.value);
    };
    
    document.body.appendChild(login);
The children call produces an array of objects that are appended to the element, but the callback also takes the wrapping element (in this case `login`), and so to be conveniently able to access it later (`login.email.value`) it also assigns them there. A more conventional JavaScript approach would be to assign email, pass and submit as local variables rather than stuffing them inside the form object.

As for the other example:

    const app = document.createElement('table');
    const tbody = document.createElement('tbody');

    app.appendChild(tbody);

    app.update = function(data) {
        tbody.innerHTML = '';
        for (let row in data.tbody) {
            let tr = document.createElement('tr');
            for (let cell in row) {
                let td = document.createElement('td');
                td.textContent = cell;
                tr.appendChild(td);
            }
            tbody.appendChild(tr);
        }
    }

    document.body.appendChild(app);

    app.update({
        tbody: [
            [1, 2, 3],
        ],
    });
This is rather simpler, frankly. I prefer plain DOM for myself at this level.


That is a great explanation and after looking at the source code and reading the example more closely that is indeed what appears to happen. I do think that, albeit very clever, it's a confusing pattern though...


And too tricky - assigning to arbitrary properties on a DOM object can have arbitrary side effects if ever you happen to overload a predefined property (on any browser).


That is an arrow function.

children() is supposed to take a function as argument and that function is supposed to return the list of children.

So basically:

children( function(el) {

  return [

    el.email = input(props({ type: 'email' })),

    el.pass = input(props({ type: 'pass' })),

    el.submit = button(text('Sign in'))

  ]
} )

Also,

[

   el.email = input(props({ type: 'email' }))

   ,

   ...
]

is a neat idea. In one (expressive) line he is assigning the value to the array and also giving the child (in this case, email) a name and reference in its parent, so later he can just call el.email (in the onsubmit function).


Yeah, I'm using similar technique with FRZR as well (https://frzr.js.org), but with RE:DOM I wanted to experiment with ES2015 and also break the API to smaller parts, which you can replace with your own if you like..


At first I was gonna say, this could be expressed with an object expression, but actually if you think about it object expressions don't grantee ordering of it's elements, that and you could probably don't need a reference to all children of a component.


    children(function(el) {
      var a, b, c;
      a = input(props({ type: 'email' }));
      el.email = a;
      // etc
      return [a, b, c];
    })
Entirely guesswork but: El is the form element you're defining children on. You need to provide references to the elements you're creating so that you can use them later (el.email and friends) but you also need to return them to the children function in an ordered manner so that it can actually attach them into the DOM.


Aah of course; By reading the source code of the children function I figured the array of elements returned was appended to the parent element. The strange thing was that it was also adding them to `el` object but indeed it appears this is done to maintain a reference to the children so they can be modified later (which I would have seen had i read the example a bit more carefully :S )

I'm still not sure how I feel about that bit of code though but it sure is clever...


Yeah, check out my another library FRZR too: https://frzr.js.org

Point is to be able to design: 1) how you create component 2) how you update it Both 1) and 2) are just simple functions.


I explain a similar case in my presentation about my another library FRZR: https://youtu.be/0nh2EK1xveg


I'd assume it's argument chaining, e.g.:

> find('elements').clear('contents').add('border')


somehow that reminds me of delete('facebook').lawyer('up').hit('thegym') ;)


Why are we creating HTML from within Javascript?

It would be much better to use HTML for that and simply toggle the display/visibility of DOM elements via CSS or JS.


We are creating HTML from within Javascript because:

- We want to generate HTML from our data (eg. database) - We want an interactive experience (eg. with toggling visibility)

You could argue that we should generate the HTML on the backend, but then you are still generating HTML, and now you have two systems that interact with the HTML instead of one.


Why not use one of the dozens of templating libraries?


Because templating languages are without exception bug-breeding grounds that no human being can actually use without producing errors, which the browser then sometimes proceeds to auto-correct (html being html), which leads to a kind of "approximate" programming style, aka nirvana for security holes and other bugs. Oh, and composition is terrible pain too, encouraging over-complex UI components that do it all. And learning whatever flavor cleverly-insufficient control-flow and looping constructs this templating library uses is a pointless waste of time (and source of bugs) too.

Why in the heavens would you ever choose to use a templating library and blur the lines between content (user input) and UI structure? Why reinvent the control-flow wheel?

Templating represents programmers realizing that this newfangled browser thing can already parse HTML, and hey, can't we do something clever with that - sure, if we unnecessarily serialize to a string first, yes! No thank you.


I straight up prefer templating for ergonomic reasons, especially when I see someone inlining higher order functions & JS control flow structures in a HTML DSL.

But I can sympathise with where you're coming from, as I have not so fond memories of backbone + [insert templating library]. A lot the problems can be addressed through better tools & a pre compilation stage where the templates are transformed into JS & data input are escaped for security reasons. One example of this is Ember.js's HTMLbars which address basically all of what you just listed there.

edit: That said if for whatever reason I couldn't use Ember, I'd probably choose some flavour of JSX over most other templating libraries, lol


Toggling visibility (and other stuff) is best done by changing classes on the element.


I think you should consider the trade-off.

If you toggle visibility with CSS, you still have to put everything in the DOM. Sometimes, putting things in DOM can be very expensive. Consider a date picker, a fairly complicated thing with many sub-elements. If you have to put thousands of date pickers in the dom, that's going to take a lot of time. Especially if the user only sees one if they click on an event to edit it.


Why not create one and move it around as necessary? I'm pretty sure that's what most date picker libraries do. Although you'd probably be moving it's location with js.


Absolutely this! Instead of awkwardly building DOM in JavaScript just build it in HTML, manipulate in JavaScript and style in CSS. This separation still works amazingly even if popular frameworks like React are blurring the lines.


Wannabe React. Engineers who over engineer.


The alternative being templating which is several orders of magnitude more complex. Fortunately, it saves you a few characters here and there.


HTML is dead. Long live HTML!


Reminds me little bit like Mithril...

Edit: Not sure why I got downvoted, but if you look at the Mithril API (sample code on http://mithril.js.org/), it's very similar to this RE:DOM library with the major difference being that there is a controller function in the Mithril example and that Mithril is more of a functional language.


I'm torn.

On the one hand this seems far too clever for it's own good and in the same vein as coffeescript where it's really easy for you to write efficient & compact code which somehow turns into complete garbage when you try to read it two weeks later.

On the other hand it looks super neat, has 0 dependencies and looks like React but without requiring any kind of build system.

I think I might need to try it in a side project.


If you're looking for a very small (~10K), dependency-free alternative to react, check out http://mithril.js.org/, which is far more powerful and from which it seems this project has taken some inspiration (mount, props, and the general syntax are a few of the similarities I see).


If you like React but want to avoid a build system, you can always do

  var form = React.createFactory('form');
  form({...}, ...);
or

  var e = React.createElement;
  e('form', {...}, ...);


Check out my other project as well: https://frzr.js.org - it's quite similar but with a bit easier syntax :)


RiotJS lets you keep the "normal" HTML and just add easily-called functions. Looks much more simple and "normal" to me than all this react stuff and HTML generation from javascript.


Riot is great, I'm one of the contributors also. Just like to experiment with things and created FRZR, RZR and now this. The main issue with Riot is, that it's still rather slow. And complex, if you want to understand 100% how it works under the hood.


>The main issue with Riot is, that it's still rather slow..

Riot is fantastic, especially in anticipation of web components, but personally I don't see performance as the main issue; I'm willing to trade performance for syntax I like, and the areas where Riot tends to choke are generally on tasks I typically wouldn't ask of client-side JS, no matter how fast it was.

It's the lack of certain critical features that kills it for me. The biggest one is that is has no Vue-style transition system for "if" and "show" -- there are plugins that do it strictly for mount and unmount. It's an outstanding question in #1858, though it might be outside the scope of Riot core, TBH.

In any case, I moved to Vue for that reason, and like it a lot, though I'd still prefer a Riot-with-easy-transitions :)


Vue is great as well! I like to know how things work also under the hood, so debugging/profiling on every level is much easier.


There is hyperscript which is a bit more "mature": https://github.com/dominictarr/hyperscript#example

There is virtual-hyperscript which ports the above to virtual-dom: https://github.com/Matt-Esch/virtual-dom/tree/master/virtual...

I made a similar-ish "library" about an year ago: https://gist.github.com/awalGarg/8a0e18c6fe87456d885f

And there are all sorts of similar DSLs you can find on npm/github for JS. In a bit less time, you can write your own. In even lesser time, you can just do what you used to do prior to all this. This does however seem like a pretty popular idea in the JS community which keeps getting "discovered" every now and then.


There is a trend in javascript community to use es2015 for everything and sometimes developers forget to keep a very fragile balance between expressiveness and simplicity.


Check out FRZR: https://frzr.js.org – RE:DOM is all about experimenting with ES2015. And actually the whole core of both projects are ES5 – you don't have to use the new syntax.. ;)


I've made a similar sort of DOM DSL: https://github.com/TazeTSchnitzel/jInsert

e.g.

  document.body.appendChild($('form', {action: '/submit', method: 'POST'}, [
      $('input', {type: 'text', name: 'username'}),
      $('br'),
      $('input', {type: 'password', name: 'password'}),
      $('br'),
      $('input', {type: 'submit'})
  ]));
RE:DOM looks cool too.

Since using Haskell's Blaze, I've wanted something close in other languages.


Good try. Although jQuery chaining api seems better for me.


is there any compile-to-javascript languages which would be termed as "disgusting" and "impure"?

https://github.com/dropbox/pyxl for python comes to mind as something more enjoyable than manually doing tree-shaped OOP

    login = : //"read variable from indented block" symbol of magicness
        <form>
            <input type=email>
            <input type=pass>
            <button>{_("Sign in")}<button> //gotta have an excuse to inline code!
        </form>
    login.events({
        onsubmit (whatever, i dunno) {
            blap
        }
    });
seems a lot more fun!


the website's UI looks great. simple and to the point.


Why the indirection creating types using el()? Why not create the nodes directly with el()?


check out frzr.js.org - I'm using that aproach there..


Still i don't understand why people writing simple HTML in complex way!


When you're writing a large application, things can get quite complicated with just HTML..



From the page >Writing HTML is stupid. It's slow, messy, and should not be done in JavaScript.

What a bold statement !


I had a similar attempt.

http://m1el.github.io/jsonht.htm


Why is this better than working with d3 selections?


Nice! reminds me of hyperscript-helpers


Unreadable.


Try my other project, FRZR: https://frzr.js.org – RE:DOM is more of an experiment in this point..




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: