Permalink
Browse files

Added JsonML support

  • Loading branch information...
1 parent d34435e commit 848318c5d1a9fcfa47b6a9bf407bfe7b35dddf98 @fgnass committed Apr 5, 2012
Showing with 127 additions and 74 deletions.
  1. +54 −42 README.md
  2. +49 −31 mkay.js
  3. +24 −1 test.html
View
@@ -10,69 +10,81 @@ Because building DOM fragments with plain jQuery is [slow](http://jsperf.com/inn
$('<div id="foo" class="bar">').text('Hello').appendTo('body')
```
-With m'kay this boils down to:
+With M'kay this boils down to:
```javascript
$('body').mk('#foo.bar', 'Hello')
```
-But there's even more! With m'kay you can ...
+But there's even more! With M'kay you can ...
* write logic in JS rather than some awkward templating language
* save node references upon creation instead of querying the DOM later
* validate your code with JSHint & Co.
* relax – no innerHTML == no XSS
-## Usage
+## Usage Examples
-`$.mk(expression [, child, ...])`
+ $.mk() <div></div>
+ $.mk('h1') <h1></h1>
+ $.mk('.foo') <div class="foo"></div>
-Returns a jQuery/Zepto chain with a new element created from an expression.
+ $.mk('h1#title') <h1 id="title"></h1>
+ $.mk('h1#title.big') <h1 id="title" class="big"></h1>
+ $.mk('input[type=text][name=foo]') <input type="text" name="foo">
-Additional arguments will be appended to the newly created element.
-If a string is passed as child it will be converted into a TextNode.
-All other types are directly passed to jQuery's
-[.append()](http://api.jquery.com/append/) method, hence DOM nodes,
-jQuery objects and functions are supported.
+As you see, you can specify the tag-name, class, id as well as attributes in a
+familiar, CSS-like syntax.
-`.mk(expression [, child, ...])`
+### Nested Elements
-Appends and returns the newly created element. This allows you to set
-attributes or CSS properties of the new element with jQuery's built-in
-methods:
+To append a text-node to new element, just pass it as additional argument:
-```javascript
-$('body').mk('#foo').css('font-size', 'x-large');
-```
+ $.mk('h1', 'Hello World') <h1>Hello World</h1>
-## Going up again
+In order to append elements you can use nested calls to `$.mk()`:
-You can access the parent chain using the `_` property. This allows you to create deeply nested tree structures with one single expression:
+ $.mk('ul', <ul>
+ $.mk('li', 'One'), <li>One</li>
+ $.mk('li', 'Two') <li>Two</li>
+ ) </ul>
-```javascript
-$('body')
- .mk('ul#foo')
- .mk('li', 'Hello')._
- .mk('li', 'World')._
-._.mk('ol#bar')
- .mk('li.even', 'One')._
- .mk('li.odd', 'Two');
-```
+### JsonML
+
+For brevity you may also use [JsonML](http://jsonml.org):
+
+ $.mk('ul', <ul>
+ ['li', 'One'], <li>One</li>
+ ['li', 'Two'] <li>Two</li>
+ ) </ul>
+
+Since M'kay fully supports the JsonML syntax, you can also set attributes by
+passing an object as second parameter:
+
+ $.mk('a', {href: 'http://github.com'}, 'GitHub')
+
+
+### Multiple Elements
+
+In order to create a chain containing multiple elements the first element
+must be an array:
+
+ $.mk(['span'], ['span']) <span></span><span></span>
+ $.mk(['em', 'Hello'], ' world') <em>Hello</em> world
+
+### Create & Append
+
+A common task is to append a newly created element to an existing one. M'kay
+therefore provides a plugin method that does just that:
+
+ $('body').mk('h1', 'Hello!')
+
+The `.mk()` plugin returns a chain containing the newly created element. This
+allows you to easily add event listeners like this:
+
+ $('body').mk('.btn', 'Click Me')
+ .click(function() { alert('click') })
-The resulting HTML will look like this:
-
-```html
-<body>
- <ul id="foo">
- <li>Hello</li>
- <li>World</li>
- </ul>
- <ol id="bar">
- <li class="even">One</li>
- <li class="odd">Two</li>
- </ol>
-</body>
-```
## License
View
80 mkay.js
@@ -1,45 +1,63 @@
//github.com/fgnass/mkay
-(function(document, $) {
+!function(document, $) {
/**
* Lightweight Jade/HAML-like DOM builder for jQuery or zepto.js.
*/
+
var re = {
tag: /^(\w+)/,
id: /#([\w_\-]+)/,
cls: /\.([^#\[]+)/,
- attr: /\[(.+?)=(.+?)\]/g
-};
-
-$.mk = function(s) {
- var tag = (re.tag.exec(s||'')||[,'div'])[1];
- var el = document.createElement(tag);
- var chain = $(el);
- var a = chain.append;
- var id = re.id.exec(s);
- var cls = re.cls.exec(s);
-
- if (id) (el.id = id[1]);
- if (cls) (el.className = cls[1].replace('.', ' '));
-
- var m;
- while ((m = re.attr.exec(s))) {
- el.setAttribute(m[1], m[2]);
- }
-
- for (var i=1; i < arguments.length; i++) {
- var n = arguments[i];
- if (typeof n == 'string') n = document.createTextNode(n);
- $.isArray(n) ? a.apply(chain, n) : a.call(chain, n);
+ attr: /\[(.+?)=(.+?)\]/g,
+ event: /^on(.+)/,
+ dots: /\./g
+}
+
+function mk(jsonml) {
+ if (!$.isArray(jsonml)) return document.createTextNode(jsonml)
+ var s = jsonml[0]
+ , attrs = jsonml[1]
+ , tag = (re.tag.exec(s||'')||[0,'div'])[1]
+ , id = re.id.exec(s)
+ , cls = re.cls.exec(s)
+ , el = document.createElement(tag)
+ , chain = $(el)
+
+ if (id) (el.id = id[1])
+ if (cls) (el.className = cls[1].replace(re.dots, ' '))
+
+ // Set attributes defined in the expression
+ var m
+ while (m = re.attr.exec(s))
+ el.setAttribute(m[1], m[2])
+
+ // If the 2nd argument is omitted splice an empty object
+ if (!$.isPlainObject(attrs))
+ jsonml.splice(1, 0, attrs = {})
+
+ // Set attributes passed as 2nd arg
+ chain.attr(attrs)
+
+ for (var i=2; i < jsonml.length; i++) {
+ var n = jsonml[i]
+ if (n.appendTo) n.appendTo(el)
+ else chain.append(mk(n))
}
+ return el
+}
- return chain;
-};
+$.mk = function(jsonml) {
+ var args = Array.prototype.slice.call(arguments)
+ if (!$.isArray(jsonml)) jsonml = args // make the outer [] optional
+ else if (args.length>1) return $($.map(args, mk)) // multiple elemens
+ return $(mk(jsonml))
+}
$.fn.mk = function(s) {
- var child = (this.constructor.mk || $.mk).apply(this, arguments);
- child._ = this.append(child);
- return child;
-};
+ var child = (this.constructor.mk || $.mk).apply(this, arguments)
+ child._ = this.append(child)
+ return child
+}
-})(document, this.jQuery || this.Zepto);
+}(document, this.jQuery || this.Zepto)
View
@@ -59,7 +59,7 @@
});
test("Child nodes as array", function() {
- $('#bar').mk('a', ['a', $.mk('b', 'b')]);
+ $('#bar').mk('a', 'a', ['b', 'b']);
equal($('#bar').html(), '<a>a<b>b</b></a>', 'Inner HTML must match');
});
@@ -82,6 +82,29 @@
equal(el.name, 'foo', 'name must be set to foo');
})
+test("JsonML", function() {
+ var el = $.mk(
+ ['a', {href: '#'},
+ 'a',
+ ['b',
+ 'b',
+ ['i','i']
+ ],
+ 'c'
+ ])
+ , div = $.mk('', el)
+
+ equal(div.html(), '<a href="#">a<b>b<i>i</i></b>c</a>', 'Inner HTML must match')
+});
+
+test('Multiple elements', function() {
+ var el = $.mk(['a'], ['b']);
+ ok(el && el.length, 'Must return a jQuery object');
+ equal(el.length, 2, "Chain must contain 2 elements");
+ equal(el.get(0).tagName, 'A', 'First element must be `A`');
+ equal(el[1].tagName, 'B', 'Second element must be `B`');
+})
+
});
</script>
</head>

0 comments on commit 848318c

Please sign in to comment.