framewreck
When jQuery gets too bulky – wreck it up with framewreck.
A lightweight modular JavaScript framework.
Main features:
- Minified and GZipped only ~2 kb
- CSS Selectors
- CSS Manipulation
- DOM Manipulation
- Traversing
- AJAX-Requests
- Events
- Data
- Chaining
Optional features:
Install with Bower
$ bower install framewreck
Embed via CDN
You can use this generator to choose from all available modules and generate a dynamic URL.
main modules
<script src="//cdn.jsdelivr.net/g/framewreck"></script>
all modules
<script src="//cdn.jsdelivr.net/framewreck/latest/framewreck.all.js"></script>
or
minc.js
module wise with<script>
// asynchronously load other modules
Minc([
'//cdn.jsdelivr.net/framewreck/latest/modules/animate/fw.animate.min.js',
'//cdn.jsdelivr.net/framewreck/latest/modules/dom/fw.dom.min.js',
'...'
]).done(function() {
// ...
});
</script>
Modules
Core: CSS Selectors
// IDs and classes
F('#id');
F('#id').find('.class');
F('#id .class').find('a');
// Index selectors
F('#id .class:0')
F('#id:1 .class:0 p')
// Pseudo classes
F('#id .class:first-child');
F('#id .class').find('a:first-of-type');
F('#id .class').find('a:last-of-type');
Selector methods
// Index selector
F('#id .class').get(1);
// Next / Previous siblings
F('#id').next();
F('#id').prev();
// Parent elements
F('#id').parent();
CSS module
// Set CSS
F('#id').css({ display: 'block', 'background-color': 'blue', padding: 10, width: 200 });
F('#id').css('background-color', 'blue');
// Get CSS
var cssValue = F('#id').css('padding') // outputs '10px'
DOM module
// Output HTML of a selector
var html = F('#id').find('.class').html();
// Set HTML of a selector
F('#id').find('.class').html('<a href="#">Link</a>');
// Append HTML/Text to a DOM-Element
F('#id').append('<a href="#">Link</a>');
// Move an existing DOM-Element to another
F('#id-2').appendTo('.class-1');
// create HTML-Elements in F() constructor
F('<a href="#">Link</a>').appendTo('body');
// Set/get value of an input/textarea
F('input').val('value');
var value = F('input').val();
// Set/get Select selected value
F('body').find('select').val(1)
var value = F('body').find('select').val();
// Checkbox / Radio buttons
F('input.checkboxOrRadio').checked(true); // checks a checkbox or radio button
F('input.checkboxOrRadio').checked(false); // unchecks a checkbox or radio button
F('input.checkboxOrRadio').checked(); // return checkbox or radio button state
// Serialize
F('form').serialize(); // output an object containg all serialized form-field values
// Add / Remove classes
F('#id').addClass('myClass');
F('#id').removeClass('myClass');
// Remove elements
F('#id').remove();
// Attributes
F('#id').attr('id'); // return attribute
F('#id').attr('class', 'myClass'); // set attribute
F('#id').attr('data-test', 'myData'); // set custom attribute
Event module
Add / Remove / Trigger default or custom events using the browsers native event-system.
var eventHandler = function(e) {
console.log(e, e.detail); // output event and given parameter object
};
// common events
F('a.button').on('click', eventHandler);
F('a.button').on('mouseover', eventHandler);
...
// custom events
F('#id').on('myEvent', eventHandler);
F('#id').trigger('myEvent', { name: '@misantronic' } );
F('#id').off('myEvent', eventHandler);
AJAX module
Do asynchronously XHR-Requests via GET/POST
F().ajax(
'get',
'http://server.com/api?id=1337',
function(e) {
console.log(e.responseText)
});
F().ajax(
'post',
'http://server.com',
function(e) {
console.log(e.responseText)
},
{ name: '@misantronic' }
);
Data module
The data module enables saving all types of data to the context using the dynamic DOM attribute structure.
F('#id').data('myObject', { name: '@misantronic' });
F('#id').data('myObject') // outputs { name: '@misantronic' }
Chaining
Any method returning F
can be chained to the next.
F('#id')
.find('.class')
.html('test')
.on('event', eventHandler)
.trigger('event', { name: '@misantronic' } )
.off('event', eventHandler);
Animate module
The animate module makes CSS3-Transforms and Animations super-easy using a unique synatx.
The commands in the animation queue are invoked at the same time ( [ 'X:5 Y:5 W:10' ]
) or one after another ( [ 'X:5' , 'Y:5', 'W:10' ]
).
Note: This module is optional and not included in dist/framewreck.min.js
Dependencies: CSS, Events, Data
Basic examples
// animate( Array animationQueue )
// syntax: propery:value[[,duration],delay]
F('#id').animate( ['O:0'] ); // changes #id's opacity to 0
F('#id').animate( ['X:100'] ); // translates x #id 100 pixels
F('#id').animate( ['X:100 Y:100'] ); // translates x/y #id 100 pixels
F('#id').animate( ['X:100 Y:100', 'O:0.5', 'W:100'] ); // queuing: translates x/y #id 100 pixels, after that change the opacity to 0.5, after that change the width to 100px
F('#id').animate( ['S:2'] ); // scale #id to factor 2
F('#id').animate( ['R:30'] ); // rotate #id 30 deg
F('#id').animate( ['X:100,2,1'] ); // translates x #id 100 pixels with a duration of 2s and a delay of 1s
Shorthands
$('#id').hide(); // non-animated hiding of #id
$('#id').show(1); // fade in #id with a duration of 1s
$('#id').hide(0.5); // hide #id with a duration of 0.5s
Callback
F('#id').animate( [ 'O:0' ], function() { // is invoked as soon as the animation stops
this.css({ display: 'none' })
});
Easing
A cheatsheet with available easing functions can be found at easings.net.
F('#id').animate( ['X:100'], 'ease-out' );
F('#id').animate( ['X:100'], function() {
alert("done")
}, 'ease-out' );
Notes
All translations are relative to the contexts initial position
F('#id').animate( ['X:100', 'X:100'] ); // #id is still at 100
Property Overview
Property | CSS | Example |
---|---|---|
O | opacity | F('#id').animate( ['O:0.5'] ) |
X | translateX() | F('#id').animate( ['X:50'] ) |
Y | translateY() | F('#id').animate( ['Y:-10'] ) |
R | rotate() | F('#id').animate( ['R:-90'] ) |
S | scale() | F('#id').animate( ['S:3'] ) |
W | width | F('#id').animate( ['W:100'] ) |
H | height | F('#id').animate( ['H:100'] ) |
P | padding | F('#id').animate( ['P:10'] ) |
PT | padding-top | F('#id').animate( ['PT:10'] ) |
PR | padding-right | F('#id').animate( ['PR:10'] ) |
PB | padding-bottom | F('#id').animate( ['PB:10'] ) |
PL | padding-left | F('#id').animate( ['PL:10'] ) |
M | margin | F('#id').animate( ['M:10'] ) |
MT | margin-top | F('#id').animate( ['MT:10'] ) |
MR | margin-right | F('#id').animate( ['MR:10'] ) |
MB | margin-bottom | F('#id').animate( ['MB:10'] ) |
ML | margin-left | F('#id').animate( ['ML:10'] ) |
Binding module
The binding-module makes it possible to bind values of DOM-elements in a bidirectional way.
Note: This module is optional and not included in dist/framewreck.min.js
Dependencies: Events, Dom
Howto
See the example at jsfiddle.net
Define a binding-element which is suppossed to be filled with data on your view.
You can also define a form-element, which will be connected to your binding-element.
<!-- Binding-element -->
Hi, my name is <span data-bindable="name"></span>
<!-- Form-element -->
<input type="text" id="inp_name">
Now, you bind your data to the binding-element
// register binding-element 'name', fill it with '@misantronic' and connect it to #inp_name
F('#inp_name').registerBindable( 'name', '@misantronic' );
The binding-element name
will now be filled with your data.
You can now get/set data:
// update binding-element (and form-element)
F().setBindable( 'name', 'David Skx' );
// get binding-element value
F().getBindable( 'name' );
Since #inp_name
is connected to the binding-element, it will automatically update its value everytime you change it.
Template-Engine
The Template-Engine is inspired by common engines like Mustache or Handlebars
but wrecked down to ~1.5 Kb.
Templates are wrapped in <script type="x-tmpl-framewreck"></script>
-tags or being loaded from external files via AJAX.
The whole syntax is an easier version of the popular handlebar-{{...}}
-style.
Note: This module is optional and not included in dist/framewreck.min.js
Dependencies: AJAX (if you want to use loadTemplate
)
Template-Example
<!-- Example for a template -->
<script id="template" type="x-tmpl-framewreck">
<h1>{{title}}</h1>
{{#if description}}
<p>{{description}}</p>
{{/if}}
{{projects}}
<div class="project">
<h2>{{name}}</h2>
<bockquote>
<p>{{quote}}</p>
</bockquote>
{{#if versions}}
<h3>Versions:</h3>
{{versions}}
<p>v.{{ value}}</p>
{{/versions}}
{{#else}}
<p>NO Versions</p>
{{/if}}
{{#if tests}}
<h3>Tests:</h3>
{{tests}}
{{#if show == 5}}
<p>
Test ran on {{run}}.
</p>
{{/if}}
{{/tests}}
{{/if}}
{{#if ! tests}}
No Tests
{{/if}}
{{#if not tests}}
Really no Tests
{{/if}}
<h3>Code:</h3>
<pre><code class="lang-javascript">{{{code}}}</code></pre>
</div>
{{/projects}}
</script>
Run Parser:
var context = {
title: '512byt.es',
description: '<a href="https://github.com/misantronic">@misantronic</a>\'s javascript <a href="http://en.wikipedia.org/wiki/Code_golf">code golf</a> projects.<br>',
projects: [
{
name: 'Invasion',
quote: 'A Space Invader clone. Invaders from outer space are coming to kill your mom! (501 bytes)',
code: 'p=389;$=l=m=t=0;c=" _ ";onkeydown=function(e){(k=e.which)==39?p++:k==37?p--:!l&(l=p)};setInterval(\'_="<pre>";l&(l-=20)<0&&(l=0);m=(m+=20)>p?b[+new Date%6]:m;for(i=0;i<400;i++){if(i%20==0)_+="\\n";if(~b[n="indexOf"](l))b.splice(b[n](l),1),$+=5,l=0;if(~b[n](p)||p==m)p=n,b=[],c="xxx";_+=i==p?"oIo":~b[n](i)?".#.":i==m&&m?" * ":i==l&&l?" | ":c}document.body.innerHTML=_+="\\nP "+$;t+=o;for(i in b)b[i]+=t%5e3==0?20:t%2e3==0?1:t%1e3==0&&-1\',o=50);for(b=[],j=2;j<136;j+=j==14||j==94?29:j==55?27:2)b.push(j)',
tests: [
{ run: "2014-11-05", show: 5 },
{ run: "2014-11-04", show: 0 },
{ run: "2014-11-03", show: 0 }
],
versions: [
"1.0.0", "1.1.0", "1.2.0"
]
},
{
name: 'Tron',
quote: 'Destroy your friends! Competitive 1on1 Tron-clone. (757 bytes)',
code: 'a=A=0;with(c.getContext("2d"))onkeyup=function(e){d=(k=e.which)==39?2:k==37?4:k==38?1:k==40?3:d;D=k==87?1:k==68?2:k==83?3:k==65?4:D;k==32&&X&S()},(S=function(){X=0;w=[{x:795,y:400}];d=1;W=[{x:5,y:0}];D=3;v=setInterval(\'c.width=c.width;p1[H="innerHTML"]=A+=z(w,d,W,"blue",0);p2[H]=a+=z(W,D,w,"red",1);if(X)clearInterval(v)\',60)})(),z=function(f,g,F,B,b){h={x:f[l=f[m="length"]-1].x,y:f[l].y};beginPath();L=lineWidth=10;h.x+=g==2?L:g==4&&-L;h.y+=g==3?L:g==1&&-L;if(F[I="filter"](t=function(o){return o.x==(T=this).x&o.y==T.y},h)[m]||f[I](t,h)[m])return X=1;strokeStyle=_=createLinearGradient(0,0,800,0);for($ in _);_[$](b,"magenta");_[$](.3,"#AFD2E6");_[$](.6,"#FF1493");_[$](!b,B);for(i=f.push({x:h.x,y:h.y})-1;i--;){lineTo(f[i].x,f[i].y)};stroke();return 0}'
},
{
name: 'Script Loader',
quote: 'Dynamically load scripts (131 bytes)',
code: 'function l(a,c,f){with(document)for(;(f=createElement(\'script\')).src=a.shift();head.appendChild(f))f.onload=function(){c&&c(this)}}'
}
]
};
F('#template').template(context).appendTo('body');
In this example, accessing the {{projects}}...{{/projects}}
-Tag actually invokes an each-loop which iterates through the projects
-array,
defined in context
. It will outputs three project-blocks.
Helper functions
You can define your own helper functions which are accessible from any context in any template.
// example for a Link-Helper
F('#template')
.registerHelper('link', function(href, title, target) {
target = target || '_self';
return '<a href="'+ href +'" title="'+ title +'" target="'+ target +'">'+ title +'</a>'
})
.template(context).appendTo('#page-wrapper');
<!-- Link-Helper Template -->
<script id="template" type="x-tmpl-framewreck">
{{link "http://512byt.es" "Visit 512byt.es" "_blank"}}
</script>
Loading external templates
// define context
var navContext = {
menu: [
{ name: 'Home', link: '/home' },
{ name: 'Projects', link: '/projects' }
]
}
// load template
F().loadTemplate('templates/nav.tpl.html', function() {
// this is an F-instance containing the template
F('#nav-wrapper').append(this);
}, navContext);
Contact and comments
If you have any questions, praise or contempt feel free to write me: misantronic@posteo.se.
Of course, you can file an issue if you find evil bugs.
For the sake of keeping things simple and small, I did not take care of optimizing my scripts for crappy browsers like IE8 etc.