Giter VIP home page Giter VIP logo

crel's Introduction

crel

NPM version Downloads

What

A small, simple, and fast DOM creation utility

Why

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

The best way to make DOM elements is via document.createElement, but making lots of them with it is tedious.

Crel makes this process easier.

Inspiration was taken from laconic, but Crel wont screw with your bad in-DOM event listeners, and it's smaller, faster, etc...

Changelog

View changelog

Installing

For browserify:

npm i crel
let crel = require('crel');

For AMD:

require.config({paths: { crel: 'https://cdnjs.cloudflare.com/ajax/libs/crel/4.0.1/crel.min' }});
require(['crel'], (crel) => {
    // Your code
});

Using ES6+:

import crel from 'crel';

For standard script tag style:

<script src="crel.min.js"></script>

Usage

Syntax:

// Returns a DOM element
crel(tagName / domElement, attributes, child1, child2, childN);

where childN may be:

  • a DOM element,
  • a string, which will be inserted as a textNode,
  • null, which will be ignored, or
  • an Array containing any of the above

Examples

let element = crel('div',
    crel('h1', 'Crello World!'),
    crel('p', 'This is crel'),
    crel('input', { type: 'number' })
);

// Do something with 'element'

You can add attributes that have dashes or reserved keywords in the name, by using strings for the objects keys:

crel('div', { 'class': 'thing', 'data-attribute': 'majigger' });

You can define custom functionality for certain keys seen in the attributes object:

crel.attrMap['on'] = (element, value) => {
    for (let eventName in value) {
        element.addEventListener(eventName, value[eventName]);
    }
};
// Attaches an onClick event to the img element
crel('img', { on: {
    click: () => {
        console.log('Clicked');
    }
}});

You can pass already available elements to Crel to modify their attributes / add child elements to them

crel(document.body, crel('h1', 'Page title'));

You can assign child elements to variables during creation:

let button;
let wrapper = crel('div',
    button  = crel('button')
);

You could probably use Crel to rearrange existing DOM elements..

crel(someDiv, crel(someOtherDiv, anotherOne));

But don't.

Proxy support

If you are using Crel in an environment that supports Proxies, you can also use the new API:

let crel = require('crel').proxy;

let element = crel.div(
    crel.h1('Crello World!'),
    crel.p('This is crel'),
    crel.input({ type: 'number' })
);

Browser support

Crel uses ES6 features, so it'll work in all evergreen browsers.

Goals

Easy to use & Tiny

Less than 1K minified, about 500 bytes gzipped === Smal

Fast

Crel is fast. Depending on what browser you use, it is up there with straight document.createElement calls: http://jsperf.com/dom-creation-libs/10

License

MIT

crel's People

Contributors

ap avatar fluvf avatar korynunn avatar lpellegr avatar marianoguerra avatar marijnh avatar mauricebutler avatar parameme avatar pdehaan avatar pocke avatar ritschwumm avatar uniphil 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

crel's Issues

Jsperf

Actually the jsperf is not quite correct comparence of pure .innerHTML to your solution. Because in your and some others case, you have references to the dom nodes. Which means after .innerHTML you need to find references, which also costs time. I would love to see a bench like that.

problem with instaceof

Hi there, I love your work with crel. I think I found a bug.

Testing in IE8 made me find the bug in this line:

var isNode = typeof Node === 'object'
    ? function (object) { return object instanceof Node }

try running this code in any browser's console:
var a = new Object();
var b = new Object();
a instanceof b;

you'll get an error saying that the expected operand is a function. A constructor, to be precise. Your code checks if Node is an object and then does an instanceof Node check with it. So it cannot ever by a function. Here's the code that fixes it:

return object instanceof Node.constructor

Sorry, I did not fork & commit this fix. I want you to see the problem and decide if I was right or I just misinterpreted something.

Performance

crel is fast. Depending on what browser you use, it is up there with straight document.createElement calls.

That part of the README seems to date back to 2012, the jsperf link is dead because jsperf is dead, so I can't really see the numbers there.

I've been fixing performance in FRZR and RE:DOM lately, and I'm writing my own little DOM library for personal uses / research, and out of curiosity I run crel through a small benchmark of mine.

Relevant numbers:

Benching Vanilla JS <div> with children
5103ns per iteration (195962 ops/sec)

Benching Crel <div> with children
19319ns per iteration (51762 ops/sec)

Relevant codes:

bench('Vanilla JS <div> with children', function() {
    var div = document.createElement('div');

    var header = document.createElement('h1');
    header.className = 'voo-header';

    var b = document.createElement('b');
    b.textContent = 'Voo';

    header.textContent = 'Hello ';
    header.appendChild(b);
    header.appendChild(document.createTextNode('!'));

    div.appendChild(header);

    var p = document.createElement('p');
    p.textContent = 'Bacon ipsum dolor amet meatloaf meatball shank porchetta \
             picanha bresaola short loin short ribs capicola fatback beef \
             ribs corned beef ham hock.';

    div.appendChild(p);
});

bench('Crel <div> with children', function() {
    crel('div',
        crel('h1', { className: 'crel' }, 'Hello ', crel('b', 'Voo'), '!'),
        crel('p',
            'Bacon ipsum dolor amet meatloaf meatball shank porchetta \
             picanha bresaola short loin short ribs capicola fatback beef \
             ribs corned beef ham hock.'
        )
    )
});

Given what crel does, and all my various experiments, numbers closer to 7000-8000ns on my hardware should have been expected. Are you interested in PRs trimming some of the unnecessary overhead?

Escaping Attributes

How do you use attributes that use - in it? Like data-id? I get an error saying the crel javascript does not know how to handle that.

Anyone know?

createElementNS support?

I'm generating SVGs with javascript alongside my HTML generation. Currently I can't use crel for the SVG elements, because they must be created with document.createElementNS.

Would there be any chance of some feature creep to the API so I can use crel for all my javascript element creation? Maybe something like

crelNS('svg', 'svg', {'class': 'graph', 'height': 100, 'width': 200},
  crelNS('svg', 'g', {'class': 'legend', 'x': 70, 'y': 40},
    crelNS('svg', 'text', 'hello')));

or perhaps

crel.svg('svg', {'class': 'graph', 'height': 100, 'width': 200},
  crel.svg('g', {'class': 'legend', 'x': 70, 'y': 40},
    crel.svg('text', 'hello')));

I'd be happy to create a pull request for this feature.

Bug in 2.2.0 – "default" element created

Hi there, first of all crel is awesome and been using it for ages without any problems.

I just updated to crel 2.2.0 in a project using Babel 6 with es2015 and stage-3 presets, and am getting the following weird behaviour:

crel('div')
// --> <default>div</default>

crel('div', {'class': 'abc'})
// --> <default>​"div""[object Object]"</default>​

I don't know how to begin debugging this because I forked crel and ran npm test and all tests passed... I guess this must have to do with the new Proxy API support because that's the only major thing that's changed since 2.1.8, which works fine. Any pointers? Thanks!

Put crel on CDNJS

It would be great for those that need to link to this lib in the page, to be able to use a CDN such as cdnjs and then use a local fallback by checking if the global exists. Cheers.

Publish to NPM

Really think crel should be available in npm - ship it :)

crel fails to add children to iframe document

If you put an iframe on a page and do a little something like this:

var innerBody = document.getElementsByTagName("iframe")[0].contentDocument.body;
crel(innerBody, crel("h1", "hi"))

In Chrome 72, it will error with

Uncaught DOMException: Failed to execute 'createElement' on 'Document': The tag name provided ('[object HTMLBodyElement]') is not a valid name.

(It succeeds without issue in Firefox 65.)

This is due to the check where crel is trying to figure out if its argument is a node or not (see related issue #11).

In Chrome

  • innerBody instanceof iframe.contentWindow.Element == true ✔️
  • innerBody instanceof window.Element == false ❌

I expect this is an uncommon edge case, and I very much appreciate crel's dedication to keeping its implementation compact and interface simple, so I'd understand if this is a wontfix, but I figured it's worth documenting.

If we do want to fix this, I think the thing to do is provide a way to change which window crel uses to get Element and Node and document.

(Perhaps surprisingly, doing innerBody.appendChild does work with elements created using the outer document. At least with these two specific browser versions and document types. But I expect it would be safer, and easier to pass type checks, if the createElement were called on the document the elements are intended to be appended to.)

Parse Embedded Id and Classes

It would be very nice to have #hash-like id and .dot-like classes parsed on tag string.

Eg.:

crel(document.body, 
    crel('input.cool'),
    crel('button#login')
);

Allow camelCase for proxy to reflect dashes in tag name

Proxy mechanism is very convenient but I did not find a way to create tags with a dash, e.g. <my-table></my-table>. It would be convenient to use camelCase (as seen in css-loader) to proxy crel using crelProxy.myTable() over crelProxy['my-table'].

I could make a PR but somehow my editor (atom/vscode) changes white spaces and the whole file is marked as a change:
https://github.com/quirm/crel/commit/acb0c89b5660498d8b0f330331be47890dcab94d (a change https://github.com/quirm/crel/blob/master/crel.js#L159-L164)

html to crel

Hello, just started dabbling with crel. Thanks - great tool.
Just wondering if anyone has made a utility to convert html to crel code.
I have a few views I'd like to try and convert.

Was looking at this http://www.jsonml.org/, which seems to be a good start;
Hope I'm not trying to reinvent the wheel here.

onfocusout / onchange etc. issue

On Chrome at least (only tested that so far), the following works fine:

    valueEdit = crel('input', {
      type: 'text'
    });
    valueEdit.onblur=function(){console.log('zzz');};

However, this one returns an error Uncaught SyntaxError: Unexpected token ( line 1:

    valueEdit = crel('input', {
      type: 'text',
      onblur: function(){console.log('zzz');}
    });

Many events (like onclick) work fine, but not the others. Interestingly, the Chrome debugger shows onblur: undefined in the non-working case, whereas it is obviously correctly referring to an anonymous function in the working case.

Is this a crel bug?

Issue between regexp, crel and regex variables.

Lets say I have a message string which I want to detect for URLs and enclose them in anchor tags. This full message is then placed within a div element using crel.

Let's say our message is "http://foo.com" and the desired result should be enclosed within an anchor tag. The following line is similar to what I am currently doing:

msg.replace(exp, crel('a', {href: '$1', target: '_BLANK', class: 'foo'}, '$1'));

The outcome of this would not be an anchor tag, but rather it would be the normal URL found prepended by the current page URL. For instance if my page was "http://localhost/" then the string returned would be "http://localhost/http://foo.com"

I believe the $1 is being interpreted as something else within the library itself. I'm not 100% sure if it's a bug but it's something I came across and cannot figure out a work-around. I was originally using a full string such as:

msg.replace(exp,'<a href="$1" target="_BLANK" class="foo">$1</a>');

This itself works fine if I was not appending to an element using crel. The crel replica as mentioned above, causes other issues.

Edit

Apparently the previous edit is somewhat inaccurate. Take the following code for example

var msg = "http://foo.com";
var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;

var html = crel('a', {href: '$1', target: '_BLANK', class: 'foo'}, '$1');

console.log(html);
console.log(msg.replace(exp, html));
console.log(msg.replace(exp,'<a href="$1" target="_BLANK" class="foo">$1</a>'));

The result would be this:

<a href=​"$1" target=​"_BLANK" class=​"foo">​$1​</a>​
http://localhost:6022/http://foo.com
<a href="http://foo.com" target="_BLANK" class="foo">http://foo.com</a>

The last one is my desired result. Somehow crel is creating the right elements, but when used with .replace it will act strangely. I still believe it's a possible bug with crel though.

Edit 2

I've modified how the replace works and I ended up with something different. The following code:

msg.replace(exp, function(match) {
    console.log(crel('a', {href: match, target: '_BLANK', class: 'foo'}, match));
});

Will produce:

<a href=​"http:​/​/​foo.com" target=​"_BLANK" class=​"foo">​http://foo.com​

This itself looks fairly accurate. Now if I was to use that as the return for the replace:

return msg.replace(exp, function(match) {
    return crel('a', {href: match, target: '_BLANK', class: 'foo'}, match);
});

Then when it appends to the element, then output will not be an anchor tag at all but rather the output would be "http://foo.com/" (notice the forward slash).

How I append to the element is rather simple. I use the following code (data.text would be the message in question and parseUrls is the function containing all the above):

crel(messageContainer,
    crel('div', parseUrls(data.text))
);

I believe what needs to happen is the string needs to be split up as I believe crel returns a document.createElement, and even if I was to grab that innerHTML then when I append it to the container, it will escape it.

So based off of this, it may not be an actual bug but rather it is an issue I cannot figure out a work-around for.

Feature Request: Handle Embedded Object Attributes

Not sure if this is something you want to add Kory, but I just found myself trying to do this (mainly to see if it would work):

crel('div', {
  class: 'blah',
  data: {
     caption: 'A Test caption'
  }
});

To see if the HTML element generated would contain a data-caption attribute. Might be a nice feature to add at some stage if it doesn't increase the size of crel too much. Also, I can't remember if you mentioned that someone had already implemented this in another fork somewhere...

Event handlers

I'd be willing to add event handler creation to the utility (using addEventListener). Would that be a feature you'd think fits the project? If so, what kind of syntax should it have?

Unnecessarily lost IE11 support

Patch 5414591 removes the check for Proxy support, breaking this library on IE11. Was the goal here just to save a single line of code? Could we maybe revert it?

Typescript version of this awesome utility

Hi there I needed to integrate your utility into one of my projects and I converted it to typescript. This is what i have done so far in case you want to offer this version too:

export class Crel {

    private static fn: string = 'function';
    private static obj: string = 'object';
    private static nodeType: string = 'nodeType';
    private static textContent: string = 'textContent';
    private static setAttribute: string = 'setAttribute';
    private static attrMapString: string = 'attrMap';
    private static d: Document = typeof document === Crel.obj ? document : <Document>{};

    public create(ele: any, ...params: any[]): any {
        var args = arguments, //Note: assigned to a variable to assist compilers. Saves about 40 bytes in closure compiler. Has negligable effect on performance.
            element = args[0],
            child: any,
            settings = args[1],
            childIndex = 2,
            argumentsLength = args.length,
            attributeMap = Crel.attrMapString;

        element = this.isElement(element) ? element : Crel.d.createElement(element);
        // shortcut
        if (argumentsLength === 1) {
            return element;
        }

        if (!this.isType(settings, Crel.obj) || this.isNode(settings) || this.isArray(settings)) {
            --childIndex;
            settings = null;
        }

        // shortcut if there is only one child that is a string
        if ((argumentsLength - childIndex) === 1
            && this.isType(args[childIndex], 'string')
            && element[Crel.textContent] !== undefined) {
            element[Crel.textContent] = args[childIndex];
        } else {
            for (; childIndex < argumentsLength; ++childIndex) {
                child = args[childIndex];

                if (child == null) {
                    continue;
                }

                if (this.isArray(child)) {
                    for (var i = 0; i < child.length; ++i) {
                        this.appendChild(element, child[i]);
                    }
                } else {
                    this.appendChild(element, child);
                }
            }
        }

        for (var key in settings) {
            if (!attributeMap[key]) {
                element[Crel.setAttribute](key, settings[key]);
            } else {
                var attr = attributeMap[key];
                if (typeof attr === Crel.fn) {
                    attr(element, settings[key]);
                } else {
                    element[Crel.setAttribute](attr, settings[key]);
                }
            }
        }

        return element;
    }



    private isType(a: any, type: any) {
        return typeof a === type;
    };

    private isNode(object: any) {
        if (Node === Crel.fn) {
            return object instanceof Node;
        }

        return object &&
            this.isType(object, Crel.obj) &&
            (Crel.nodeType in object) &&
            this.isType(object.ownerDocument, Crel.obj);
    }

        // in IE <= 8 Node is an object, obviously..

    private isElement(object: any): any {
        return this.isNode(object) && object[Crel.nodeType] === 1;
    }

    private isArray(a: any) {
        return a instanceof Array;
    }

    private appendChild(element: any, child: any) {
        if (!this.isNode(child)) {
            child = Crel.d.createTextNode(child);
        }
        element.appendChild(child);
    };
}

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.