In this week, what makes me so exciting is not D3 again, although I still have some skills about D3 which I would like to share, I'll talk about another front-end UI framework in this post, the Twitter Bootstrap. According to the official slogan, Bootstrap is a sleek, intuitive, and powerful mobile first front-end framework for faster and easier web development. Actually, Bootstrap is base on jQuery, and is not something new. But when I accidentally had a chance to study it, I really feel exciting.
The first selling point of Bootstrap is rapid development. It can really boost up the development of front-end design, it remedies the weakness of layout design in jQuery. Just with some well formatted HTML (no javascript), you can get a good looking and interactive web page, with navigation bar and slideshow banner or other basic website element.
The second selling point of Bootstrap is responsive web design, actually is its greatest selling point. It makes you web page fit to all kind of devices. It automatically change the look and view according to you devices's resolution, so you would get well-fit outlook adaptive to you mobile phone, tablet or PC, but would not affect you functionality.
Actually, although Bootstrap is good, it can't satisfy my desire. Because I want a responsive animated navigation bar, so I try to customize one. Through studying how Bootstrap is implemented, I learnt a lot about how to build you own jQuery widget, how to write response web design and also some tricks to play with CSS. Since it isn't quite related to Bootstrap, and I need times to simplify my code I'll do it in the next blog post.
Friday, September 27, 2013
Sunday, September 22, 2013
D3 - My first bar chart
In this post, I'll use a simple bar chart to briefly introduce how to use D3, and the concept of the 3 states in data life cycle,
Live Demo Here
In the very beginning, I declare an array
Then I define 2
Finally, we come to the main course of this post, use data-driven methodology to draw the bars. At the beginning of this function, I define a X-axis
Then it comes to the
Data Binding (line 43,44) - so you can find I use function
Data Enter (line 45) - in D3 we always bind data to DOM element, and D3 would know which data is new / insert to the dataset. So we call
Data update (line 51) - since dataset may change time to time, so base on the dataset, the DOM element may need to update. Update would include both new data and existing data, so you can see I directly change attributes on the DOM element. The
Data Exit (line 57) - it describes the data which is removed compare to the previous dataset. So what I need to do is just tell D3 to hide the bar for exiting data, and then
Lastly, I bind 2
To conclude, make use of the
The whole source code is as below:
enter
, update
and exit
. Some helper function me may touch here, such as select
, scale
, axis
and on
, but the I'll not explain them in details.Live Demo Here
var myData = [], // Bar chart's data source max = 10, // Y-axis maximum value margin = {top: 20, right: 20, bottom: 40, left: 40}, width = 400, height = 300; function randData(){ // Add random number to myData myData.push(Math.ceil(Math.random() * max)); } function resetData(){ // Reset myData myData = []; }
In the very beginning, I declare an array
myData
, which represents the data in the bar chart. Also there are some setting like, maximum value, margin of the chart, the dimension of the bar chart and 2 function to manipulate the data.var y = d3.scale.linear() // Scale of y coordinate .domain([0, max]) .range([height-margin.bottom, margin.top]); var h = d3.scale.linear() // Scale of height .domain([0, max]) .range([0, height-margin.top-margin.bottom]);
Then I define 2
scale
object. Actually it is just a function to map a input domain data, into output range data. It is useful when you need to know a bar should locate to which x,y coordinate in the <svg>
element, and how many pixel should be the high of the bar.var svg = d3.select('#result').append('svg') // Create <svg> HTML Element .attr('width', width) .attr('height', height); var xAxisLine = svg.append('g') // Create X-axis line .attr('class', 'axis') .append('line') .attr('x1', margin.left) .attr('y1', height - margin.bottom) .attr('x2', width - margin.right) .attr('y2', height - margin.bottom);The above code show how I append
<svg>
and <line>
element, then change their attributes. Actually the syntax is very similar to jQuery, I guess there is no difficulty for you to follow.var yAxis = d3.svg.axis() // D3 axis object .scale(y) .orient("left"); var yAxisLine = svg.append("g") // Create Y-axis line .attr('class', 'axis') .attr("transform", "translate("+margin.left+",0)") .call(yAxis);In previous code, you may found that I need 7 lines of code to create a single
<line>
element in order to represent the X-axis. So would it be very trouble to draw a axis with ticks and value labels? No, D3 provide a axis
object to auto generate a axis.function render(){ var x = d3.scale.ordinal() // Create scale of X-axis .domain(myData.map(function(d, i){ return i; })) .rangeRoundBands([margin.left, width-margin.right], 0.1); var bar = svg.selectAll('.data') // Bind data .data(myData); bar.enter().append('rect') // Data enter .attr('class', 'data') .attr("width", x.rangeBand()) .attr("height", h(0)) .attr('x', function(d, i){ return x(i); }) .attr('y', y(0)); bar.transition() // Data update .duration(1000) .attr("height", h) .attr("width", x.rangeBand()) .attr('x', function(d, i){ return x(i); }) .attr('y', y); bar.exit().transition() // Data exit .duration(500) .attr("height", h(0)) .attr('y', y(0)) .remove(); }
Finally, we come to the main course of this post, use data-driven methodology to draw the bars. At the beginning of this function, I define a X-axis
scale
base on the number of data.Then it comes to the
data binding
, in D3 each data element would bind to a DOM element. After data binding, we can define the actions when data enter
, update
and exit
.Data Binding (line 43,44) - so you can find I use function
data
for the selection result. This function allow 2 arguments, the first is an array of data, the second is optional, a function for identify the data element, default is a function return the array index of a data element.Data Enter (line 45) - in D3 we always bind data to DOM element, and D3 would know which data is new / insert to the dataset. So we call
enter
to tell D3 we need to do action for those new data, and immediate call append
to create a container for the data.Data update (line 51) - since dataset may change time to time, so base on the dataset, the DOM element may need to update. Update would include both new data and existing data, so you can see I directly change attributes on the DOM element. The
transition
and duration
call is mainly related to the animation for the bar shift when there is new entry data.Data Exit (line 57) - it describes the data which is removed compare to the previous dataset. So what I need to do is just tell D3 to hide the bar for exiting data, and then
remove
them from DOM tree.d3.select('input[type=button]').on('click', function(){ randData(); render(); }); d3.select('input[type=reset]').on('click', function(){ resetData(); render(); });
Lastly, I bind 2
onclick
function for the button, so we can add and reset the data, all the stuff is just the same as jQuery.To conclude, make use of the
data binding
, enter
, update
and exit
allow us to render the data representation as batch. Actually, if you need to build a customize axis, you can also trend the ticks' values as data of the axis, and render it through its own life cycle.The whole source code is as below:
<!DOCTYPE html> <html> <head> <style> svg { border: 1px solid black; } .axis line, .axis path{ fill: none; stroke: black; shape-rendering: crispEdges; } rect.data { fill: steelblue; } </style> </head> <body> <div id="result"></div> <input type="button" value="Add Data" /> <input type="reset" value="Reset" /> <script src="http://d3js.org/d3.v3.min.js"></script> <script src="main.js"></script> </body> </html>
var myData = [], // Bar chart's data source max = 10, // Y-axis maximum value margin = {top: 20, right: 20, bottom: 40, left: 40}, width = 400, height = 300; function randData(){ // Add random number to myData myData.push(Math.ceil(Math.random() * max)); } function resetData(){ // Reset myData myData = []; } // Tha setting of the bar chart var y = d3.scale.linear() // Scale of y coordinate .domain([0, max]) .range([height-margin.bottom, margin.top]); var h = d3.scale.linear() // Scale of height .domain([0, max]) .range([0, height-margin.top-margin.bottom]); var svg = d3.select('#result').append('svg') // Create <svg> HTML Element .attr('width', width) .attr('height', height); var xAxisLine = svg.append('g') // Create X-axis line .attr('class', 'axis') .append('line') .attr('x1', margin.left) .attr('y1', height - margin.bottom) .attr('x2', width - margin.right) .attr('y2', height - margin.bottom); var yAxis = d3.svg.axis() // D3 axis object .scale(y) .orient("left"); var yAxisLine = svg.append("g") // Create Y-axis line .attr('class', 'axis') .attr("transform", "translate("+margin.left+",0)") .call(yAxis); // Main function to draw the bar chart's "bars" function render(){ var x = d3.scale.ordinal() // Create scale of X-axis .domain(myData.map(function(d, i){ return i; })) .rangeRoundBands([margin.left, width-margin.right], 0.1); var bar = svg.selectAll('.data') // Bind data .data(myData); bar.enter().append('rect') // Data enter .attr('class', 'data') .attr("width", x.rangeBand()) .attr("height", h(0)) .attr('x', function(d, i){ return x(i); }) .attr('y', y(0)); bar.transition() // Data update .duration(1000) .attr("height", h) .attr("width", x.rangeBand()) .attr('x', function(d, i){ return x(i); }) .attr('y', y); bar.exit().transition() // Data exit .duration(500) .attr("height", h(0)) .attr('y', y(0)) .remove(); } // Event handler of buttons d3.select('input[type=button]').on('click', function(){ randData(); render(); }); d3.select('input[type=reset]').on('click', function(){ resetData(); render(); });
Thursday, September 19, 2013
D3 - Build chart so easy
Today I would like to introduce an interesting library, D3. D3 means "Data Driven Document", which is a javascript data visualization library. With this library, you can very easy to repersent data in a more fancy way. Here are some offical examples.
D3 strong at make use of HTML5 inline SVG to render graphic, so it is not compatible with IE8. Eventhough there is such a limitation, I found that D3 is so easy to use, with great support for animation and interaction, and its key concept of data driven is really good at ploting graph.
The following is an example of a very simple bar chart. Note that since D3 is not a plugin but it is a library, I think use 40 lines code to build a bar chart is pretty good.
Live Demo Here
In the next blog post, I'll dig in to the D3 and explian how to use.
D3 strong at make use of HTML5 inline SVG to render graphic, so it is not compatible with IE8. Eventhough there is such a limitation, I found that D3 is so easy to use, with great support for animation and interaction, and its key concept of data driven is really good at ploting graph.
The following is an example of a very simple bar chart. Note that since D3 is not a plugin but it is a library, I think use 40 lines code to build a bar chart is pretty good.
var myData = [{name:"A",value:3}, {name:"B",value:5}, {name:"C",value:2}]; var margin = {top: 20, right: 20, bottom: 40, left: 40}; var width = 400; var height = 300; var x = d3.scale.ordinal() .domain(myData.map(function(d){ return d.name; })) .rangeRoundBands([margin.left, width-margin.right], 0.1); var y = d3.scale.linear() .domain([0, d3.max(myData, function(d){ return d.value; })]) .range([height-margin.bottom, margin.top]); var h = d3.scale.linear() .domain([0, d3.max(myData, function(d){ return d.value; })]) .range([0, height-margin.top-margin.bottom]); var xAxis = d3.svg.axis() .scale(x); var yAxis = d3.svg.axis() .scale(y) .orient("left") .ticks(5); var svg = d3.select('body').append('svg') .attr('width', width) .attr('height', height); svg.append("g") .attr('class', 'axis') .attr("transform", "translate(0,"+(height-margin.bottom)+")") .call(xAxis); svg.append("g") .attr('class', 'axis') .attr("transform", "translate("+margin.left+",0)") .call(yAxis); var bar = svg.selectAll('.data').append('rect') .data(myData); bar.enter().append('rect') .attr('class', 'data') .attr("width", x.rangeBand()) .attr("height", function(d){ return h(d.value); }) .attr('x', function(d){ return x(d.value); }) .attr('y', function(d){ return y(d.value); });
Live Demo Here
In the next blog post, I'll dig in to the D3 and explian how to use.
Sunday, September 15, 2013
Just few words
After writing several blog posts, I found that it is really hard to use words to share the coding experience with others.
Hack on jQuery UI widget
If you are going build your own jQuery UI widget / plugin, you may face a problem like me, how to determine the entry and exit point of the widget.
Let's say your widget is something like a date picker, initially it is a input box, when you click on it or tab to it, it popup a picker for you, then if you click outside the input box or the picker, the widget hide the popuped picker. The life cycle is so straight forward, but actually you would find
If you bind a exit point on these two events, when you click from the input box to the popup picker, it must fire one pair of
So after I read every lines on the jQuery's Datepicker, I suggest a widget should be something like this (just pseudo code):
Let's say your widget is something like a date picker, initially it is a input box, when you click on it or tab to it, it popup a picker for you, then if you click outside the input box or the picker, the widget hide the popuped picker. The life cycle is so straight forward, but actually you would find
focusout
and blur
tell you nothing about the new focus target, so you can't treat these two event as a exit point of the UI widget.If you bind a exit point on these two events, when you click from the input box to the popup picker, it must fire one pair of
blur
and focus
event, which make you popup stuffs close once and immediate re-open again, cause a bad flicking visual effect to user.So after I read every lines on the jQuery's Datepicker, I suggest a widget should be something like this (just pseudo code):
$.widget('myWidget', { _init : function(){ // Bind the enter and exit event handler this._on(this.element, { 'focus' : this._enter }); this._on(document, { 'mousedown' : this._exit, 'focusin' : this.exit }); }, _enter : function(event){ // Entry point, create the popup picker this._picker = new Picker(); // Append to some where below the input box }, _exit : function(event){ if(event.target != this.element && !$.contains(this._picker, event.target)){ // Exit point, remove popup picker this._picker.remove() } } });
Wednesday, September 11, 2013
A simple mouse click, not easy mouse event
My very first task in my job is to code refactoring the UI widgets developed by our company (mainly some mutation of jquery UI). Through studying the current UI widgets' life cycle, I found that most of the bugs were came from mishandling of mouse events. After the hard studies of the behaviors across the 3 main browsers, IE, Firefox and Chrome, I have a small note and summary as below.
Include the jquery custom
1)
7)
This sequences is generalized for all focusable and editalbe elements, such as a button, input box, radio button, div with tabindex, etc. Lets assume A Type element is something like input box and textarea, and the B Type element is something like check box, radio button and select list. With this knowledge, there are several thing we shoud be care.
Include the jquery custom
focusin
and focusout
events, there are total 8 events which are trigger by a Single Mouse Click, and they are: mousedown
, mouseup
, focusin
, focusout
, focus
, blur
, change
and click
. Actually only the first two events, mousedown
and mouseup
, are directly related to the physical mouse left button press and release, all others are some logical events. As excepted, different browsers has its own implementation, but surprisingly different jquery version handle the events differently, as the result jquery dosen't help you to standardize the event sequences, and make things so unexpected. But after thousands of tests and experiments, I figuer out the evnet sequence should like:1)
mousedown
-> 2) change
(A Type) -> 3) focusout
-> 4&5) blur
/ focusin
-> 6) focus
7)
mouseup
-> 8&9) change
(B Type) / click
This sequences is generalized for all focusable and editalbe elements, such as a button, input box, radio button, div with tabindex, etc. Lets assume A Type element is something like input box and textarea, and the B Type element is something like check box, radio button and select list. With this knowledge, there are several thing we shoud be care.
-
The event sequence would always preserve, which means they are always fire out one by one, even if you had call
preventDefault
andstopPropagation
. But if you callpreventDefault
duringmousedown
, the 2nd to 6th events would not be fired. -
blur
andfocusin
event would come out dependent on browser, normally it isblur
->focusin
, in IE it isfocusin
->blur
, so you should choose only one of them to use in the whole system, to prevent logic error or dead lock between elements. -
For B Type elements, the
change
andclick
event sequece is also dependent on browser, IE8, IE9+, Firefox and Chrome would have totally different strange behaviors. If you use jquery to bind event handlers, the intermediate element's value would be different also. You must bind handler to change event, if you need to trace the element's intermediate value.
Sunday, September 8, 2013
Hello World
Here is my first post in blogger :)
I am little boy just leaved the school, and joined to the society, being a little web application front-end developer.
In this blog, I will share what I found interesting during my work, such like some interesting library, framework, coding methodology, etc. Sometimes, I will also write something about the bugs in browsers I have faced, and the mistake I had made which you should be care about.
Let's enjoy the blog, and let me say:
I am little boy just leaved the school, and joined to the society, being a little web application front-end developer.
In this blog, I will share what I found interesting during my work, such like some interesting library, framework, coding methodology, etc. Sometimes, I will also write something about the bugs in browsers I have faced, and the mistake I had made which you should be care about.
Let's enjoy the blog, and let me say:
console.log('Hello World'); console.log('Hi, call me KiLla');
Subscribe to:
Posts (Atom)