Tính năng Media Query của CSS3 dự định sẽ mang lại một “kỉ nguyên”
mới của kĩ thuật thiết kế web mới trong năm 2012. Nhờ tính năng, các
trang web sẽ hiển thị linh hoạt hơn trên các loại thiết bị, độ phân giải
màn hình, kích thước trình duyệt khác nhau mà không cần phải sử dụng
bất kì mã xử lý javascript nào. Kĩ thuật thiết kế này được gọi là
Responsive Web Design và được ứng dụng rất phổ biến trên thế giới.
CSS2: Media Type
Phiên bản CSS 2 mặc dù chưa hỗ trợ Media Query nhưng cũng đã hỗ trợ các Media Type. Tính năng này cho phép xác định các định dạng cho mỗi loại thiết bị nhưng không linh hoạt và rất hạn chế.
Media Type
Description
All
Used for all media type devices
Aural
Used for speech and sound synthesizers
Braille
Used for braille tactile feedback devices
Embossed
Used for paged braille printers
Handheld
Used for small or handheld devices
Print
Used for printers
Projection
Used for projected presentations, like slides
Screen
Used for computer screens
Tty
Used for media using a fixed-pitch character grid, like teletypes and terminals
Tv
Used for television-type devices
Trong cùng một style sheet, bạn có thể sử dụng các luật (hoặc chỉ thị) @media để quy định loại thiết bị nào sẽ được áp dụng bởi các khối định dạng CSS bên trong mỗi luật đó.
Ví dụ như đoạn style sheet sau sẽ thiết lập chiều rộng của trang web
là 800px khi được xem trên máy tính và 300px khi xem trên các thiết bị
cầm tay. Cả hai loại thiết bị này đều hiển thị font-weight của các thẻ
là bold.
01
02
@media screen
03
{
04
body {width:800px;}
05
}
06
@media handheld
07
{
08
body {width:300px}
09
}
10
@media screen,handheld
11
{
12
p {font-weight:bold;}
13
}
14
CSS3 : Media Query
Media Query là một bước cải tiến của luật @media bằng cách kết hợp
Media Type và các thông số khác như độ phân giải, kích thước, màu sắc,…
để tăng thêm tính chặt chẽ và linh hoạt của các luật này. Mỗi luật của
Media Query là một biểu thức logic và chỉ được áp dụng khi nó có giá trị
true.
Các Media Query có thể được sử dụng bằng các cách dùng thẻ
trong HTML hoặc thẻ trong XML,
ngoài ra thì trong HTML bạn có thể dùng các luật @import và @media. Các
ví dụ sau sẽ áp dụng style css cho các loại máy vi tính và hỗ trợ màu:
1
"stylesheet"type="text/css"href=" example.css"media="screen and (color)">
2
3
"screen and (color)"rel="stylesheet"href="example.css"?>
4
5
@import url(style.css) screenand (color);
6
7
@media screenand (color) and (min-width:400px) {
8
9
}
Một Media Query được tạo thành từ một Media Type và không hoặc nhiều expression (biểu thức) nối với nhau bằng toán tử and. Cấu trúc của một Media Query có dạng:
- Media Feature: bao gồm các thuộc tính như width, height,
device-width, device-height, color, resolution, … Các thuộc tính này
thường được thêm các tiếp đầu ngữ min- hoặc max- để xác định phạm vi ảnh
hưởng của thuộc tính trong Media Query.
number of bits per pixel in a monochrome frame buffer
resolution
resolution (“dpi” or”dpcm”)
yes
resolution
scan
“progressive”or”interlaced”
no
scanning process of ”tv”media types
width
length
yes
width of the rendering surface
- Expression: bao gồm một Media Feature và không hoặc một giá trị của nó được ngăn cách bởi dấu hai chấm “:”. Mỗi Expression được bao trong cặp ngoặc đơn.
Ví dụ: (color) và (min-width : 200px).
Nhiều expression nối với nhau bằng toán tử and: (color) and (min-width : 200px)
- Media Type: Tham khảo bảng ở phần trên.
- Media Query: Bắt đầu bằng một Media Type và theo sau là một chuỗi Expression cũng được nối với nhau bằng toán tử and.
- Các Media Query có thể được nối với nhau bằng dấu phẩy “,”, tương tự như khi dùng selector của css.
Ngoài ra bạn có thể sử dụng thêm hai toán tử là not và only vào đầu Media Query. Công dụng của hai toán tử này:
-Toán tử not giúp đảo ngược giá trị của Media Query. Toán tử này có độ ưu tiên thấp nhất nên sẽ được tính cuối cùng.
-Toán tử only giúp ẩn các style sheet trên các trình duyệt không hỗ trợ Media Query.
Demo: Thiết kế một trang web theo Responsive Design
Trình duyệt với kích thước >800px: Khi thu nhỏ width lại bạn sẽ thấy vị trí sidebar thay đổi và biến mất nếu như nhỏ hơn 400px:
Mã nguồn hoàn chỉnh:
01
<html>
02
<head>
03
<title>Responsive Design Demo</title>
04
05
<styletype="text/css">
06
header, section, aside, footer {
07
background: wheat;
08
border: 1px solid gray;
09
margin: 2px;
10
font-family: Sans-serif;
11
font-size: 1.6em;
12
font-style: bold;
13
}
14
aside, section {
15
height: 300px;
16
}
17
footer{
18
clear: both;
19
}
20
21
/* Media queries */
22
23
@media screen {
24
section {
25
width: auto;
26
float: none;
27
}
28
aside {
29
display: none;
30
}
31
}
32
@media screen and (min-width: 400px) {
33
section {
34
width: auto;
35
float: none;
36
}
37
aside {
38
width: auto;
39
height: 200px;
40
float: none;
41
display: block;
42
}
43
44
}
45
46
@media screen and (min-width: 800px) {
47
section {
48
width: 78%;
49
float: left;
50
}
51
aside {
52
width: 20%;
53
height: 300px;
54
float: right;
55
display: block;
56
}
57
}
58
</style>
59
</head>
60
61
<body>
62
<header>
63
Header
64
</header>
65
66
<section>
67
Main Content
68
</section>
69
70
<aside>
71
Sidebar
72
</aside>
73
74
<footer>
75
Footer
76
</footer>
77
</body>
78
</html>
Tham khảo cú pháp hoàn chỉnh của Media Query tại: https://developer.mozilla.org/en/CSS/media_queries
YinYang’s Programming.
(http://yinyangit.wordpress.com/2012/02/18/css3-media-query-va-responsive-web-design/)
Grid System là một thuật ngữ được dùng để chỉ phương pháp sắp đặt các
thành phần HTML theo dạng lưới dựa trên CSS. Một Grid System rất quen
thuộc và được áp dụng phổ biến là 960 Grid.
960 Grid hỗ trợ hai loại là 12 và 16 cột. Ngoài ra, bạn có thể sử dụng
một Grid System tương tự cho phép bạn xác định số cột tùy ý, đó là Variable Grid System.
960 Grid System
Xem Demo: http://960.gs/demo.html
Download: http://github.com/nathansmith/960-Grid-System/zipball/master
Cái tên bắt đầu với con số 960 là do Grid System này cố định chiều rộng
của trang web là 960px. Với loại 12 cột thì mỗi cột sẽ có chiều rộng là
60px và tương tự là 40px cho loại 16 cột. Ở đây tôi chỉ đề cập đến loại
12 cột (16 cột cũng tương tự).
Bạn có thể thắc mắc tại sao chỉ là 60px với 12 cột (bởi vì 12 * 60px =
720px)? Câu trả lời là bởi vì các cột không nằm sát nhau mà chúng cách
nhau 20px, hay nói cách khác, một cột sẽ có margin trái và phải là 10px. 12 * 60px + 12 * 20px = 960px
Mỗi ô của Grid System thường là một thẻ div (hoặc bất kì thẻ nào bạn muốn) được gắn class là grid_x.
Trong đó là x là một số xác định chiều rộng của ô (từ 1 đến 12). Ví dụ
chiều rộng của thẻ div tương ứng với mỗi class là (xem hình minh họa): grid_1: 60px grid_2: 140px grid_3: 220px …
Để chứa một Grid System, bạn cần tạo một thẻ div “container” với class có dạng container_x.
Giá trị x ở đây là số lượng cột tối đa mà Grid System hỗ trợ, nghĩa là
container_12 đối với loại 12 cột. Các ô bên trong sẽ tự động xuống hàng
mới khi vượt quá số lượng cột của container.
Để bắt buộc tạo hàng mới, bạn có thể thêm vào các thẻ div có class=”clear”.
Ví dụ sao tạo ra một lưới với ba hàng. Bạn sẽ thấy hàng thứ hai mặc dù
chỉ có một thẻ div rộng 140px nhưng nó vẫn xuống hàng mới.
01
<divclass="container_12">
02
<divclass="grid_12">
03
<p> 940 </p>
04
</div>
05
<divclass="grid_2">
06
<p> 140 </p>
07
</div>
08
<divclass="clear"></div>
09
<divclass="grid_1">
10
<p> 60 </p>
11
</div>
12
<divclass="grid_11">
13
<p> 860 </p>
14
</div>
15
</div>
Các thuật ngữ
Push và Pull:
- Mặc định các ô trong lưới sẽ nằm cách nhau theo chiều ngang một khoảng
cố định là 20px. Tuy nhiên bạn có thể muốn “đẩy” (push) hoặc “kéo”
(pull) một ô nào đó để nó nằm đè lên ô khác nhằm tạo ra một layout linh
hoạt. Grid System cung cấp cho bạn các class có tên là push_x và pull_x
(x là số cột). Ví dụ:
push_2: đẩy về bên phải 2 cột.
pull_2: kéo sang bên trái 2 cột. Prefix và Suffix:
- Quy định khoảng cách trước và sau giữa các ô. Ví dụ: prefix_1, suffix_2 Alpha và Omega
- alpha: loại bỏ margin phía trước.
- omege: loại bỏ margin phía sau.
Variable Grid System
Dựa trên 960 Grid System, bạn có thể dễ dàng tạo ra một Grid System với số lượng, kích thước của cột, gutter tại http://grids.heroku.com/:
Tóm lại, trong khi chờ đợi Flexbox của CSS3 được hỗ trợ đầy đủ, bạn nên sử dụng Grid System cho các dự án của mình để đơn giản hóa việc tạo các giao diện.
YinYangIt’s Blog
(http://yinyangit.wordpress.com/2012/07/19/css-tim-hieu-ve-grid-system/)
Unit test là công việc quen thuộc của các lập trình viên để kiểm tra
một đơn vị mã nguồn có hoạt động chính xác hay không. Nếu bạn là một
javascript coder và đang tìm kiếm một phương pháp thực hiện unit test,
hãy thử sử dụng QUnit – một framework được tạo jQuery team và được sử
dụng cho dự án jQuery.
File HTML trên tôi tạo một hàm vô danh (anonymous function)
tự động thực thi để chứa các đoạn mã sẽ viết. Phương pháp này được áp
dụng phổ biến trong lập trình javascript vì giúp tránh việc tạo ra các
định danh không cần thiết, và do đó hạn chế được các trường hợp xung đột
(conflict) định danh.
Đừng quên thêm vào thẻ div có id là “qunit”, đây sẽ là nơi để QUnit hiển
thị các thông tin và kết quả test lên trên trình duyệt. Nếu bạn mở file
này bên trong trình duyệt, bạn sẽ thấy giao diện như sau:
Các tùy chọn
Giao diện QUnit tạo ra có 3 checkbox tùy chọn. Công dụng của chúng là:
- Hide passed tests: ẩn các test chạy đúng. Việc sử dụng tùy chọn có thể khiến bạn khó xác định được test nào fail.
- Check for Globals: test sẽ là fail nếu như một biến
global (hay một thuộc tính của đối tượng window) được tạo ra khi thực
thi. Việc này nhằm phát hiện và hạn chế việc tạo ra các biến global một
cách không cần thiết và có thể gây ảnh hưởng đến toàn bộ trang web.
- No try-catch: Chạy các test mà không sử dụng khối try
catch. Tùy chọn này giúp bạn có thể phát hiện các vị trí lỗi và debug
(thường kết hợp với tool như Firebug).
Tạo các Unit Test
Trước tiên, tôi tạo một test unit, ở đây là một function add(x, y)
đơn giản có chức năng trả về tổng của hai tham số. Sau đó sử dụng hàm
test() của QUnit để bắt đầu một unit test mới với hai tham số là tên và
đoạn code chứa các mã test: test( name, expected, test )
Ở đây tôi sử dụng assertion là equal() để so sánh hai giá trị bằng nhau: equal( actual, expected, message )
01
(function(){
02
// test unit
03
functionadd(x, y){
04
returnx + y;
05
}
06
07
test('add()', function() {
08
equal(add(1, 2), 3, "Passed: 1 + 2 = 3");
09
equal(add(2, null), 3);
10
equal(add_numbers(2, 2), 3);
11
12
});
13
14
})();
Kết quả:
Bạn thấy kết quả hiển thị có thông báo “1 tests of 3 passed, 2 failed“. Bên dưới liệt kê các test có đánh số thứ tự cùng với kết quả của chúng:
1. Passed: 1 + 2 = 3
2. failed (Do giá trị Expected và Result khác nhau)
3. Died (add_numbers không được định nghĩa)
Một test có trạng thái “died” tức là mọi test bên
dưới nó sẽ không được thực hiện. Điều này xảy ra khi một lỗi nghiêm
trọng có thể ảnh hưởng đến toàn bộ test bên dưới. Bạn cũng có thể tạo ra
các test dạng này bằng cách dùng assertion ok(): ok( state, message )
Ví dụ các test unit của bạn cần đối tượng jQuery để chạy, vậy bạn có thể viết như sau:
1
test('ok() assertion', function() {
2
ok(jQuery);
3
4
// assertion calls
5
6
});
Test bất đồng bộ (Asynchronous)
Phương pháp test thông thường sẽ chạy lần lượt từ trên xuống dưới các
lời gọi assertion (test đồng bộ – Synchronous). Trong trường hợp muốn
test một cách bất đồng bộ, bạn có thể sử dụng hai hàm stop() và start()
kết hợp với setTimeout()/setInterval().
- start( decrement ) : Chạy lại test khi nó bị dừng bởi stop().
- stop( increment ) : Dừng test cho đến khi start() được gọi.
Ví dụ:
1
test('asynchronous test', function() {
2
stop();
3
4
equal(1,1);
5
setTimeout(start,1000);
6
7
});
Sẽ có kết quả là:
Tests completed in 1045 milliseconds.
1 tests of 1 passed, 0 failed.
Như bạn thấy, chỉ một lời gọi assertion đơn giản nhưng lại chiếm hơn
1000 milisecond để thực thi. Đó là do dòng lệnh equal(1,1) chỉ được thực
hiện sau khi hàm start() được gọi.
YinYangIt’s Blog
(http://yinyangit.wordpress.com/2012/07/11/javascript-unit-test-voi-qunit/)
Hiện nay mặc dù OOP đã rất phổ biến nhưng đa số vẫn còn khá mơ hồ
trong việc phân biệt và định nghĩa hai khái niệm Interface và Abstract
class. Có vẻ vấn đề này không được dạy rõ ràng ở trường, hoặc có thể các
người dạy cũng chưa nắm rõ về nó. Ngoài ra, đây còn là một vấn đề cần
giải quyết mà xác suất bạn nhận được khi đi phỏng vấn là khá cao.
Is-a và Can-do
Bỏ qua tất cả những phần về lý thuyết của việc tạo một abstract class
và interface. Bạn không cần quan tâm nhiều đến việc abstract có thể
khai báo những gì, hay interface có được phép định nghĩa nội dung phương
thức hay không. Điểm cơ bản khi bạn được hỏi về sự khác biệt giữa chúng
là gì? Đó chính là mục đích mà chúng được sử dụng:
- Abstract class: là một class cha cho tất cả các class có
cùng bản chất. Bản chất ở đây được hiểu là kiểu, loại, nhiệm vụ của
class. Hai class cùng hiện thực một interface có thể hoàn toàn khác nhau
về bản chất.
- Interface: là một chức năng mà bạn có thể
thêm và bất kì class nào. Từ chức năng ở đây không đồng nghĩa với
phương thức (hoặc hàm). Interface có thể bao gồm nhiều hàm/phương thức
và tất cả chúng cùng phục vụ cho một chức năng.
Vậy, bạn không nên nhầm lẫn khi nói về việc một class được implement hay extend.
Nhiều người thường hay đồng nhất là không phân biệt hai từ này, nhưng
chính chúng đã nói lên sự khác biệt giữa interface và abstract class.
Bạn chỉ có thể thừa kế (extend) từ một class và chỉ có thể hiện thực
(implement) các chức năng (interface) cho class của mình.
Theo cách ngắn gọn, quan hệ giữa một class khi thừa kế một abstract class được gọi là is-a, và một class khi hiện thực một interface được gọi là can-do (hoặc –able).
Hãy xem ví dụ sau, tôi có:
- Interface: Barkable, Runable, Flyable, Swimable.
- Abstract class Animal và các sub class: Bolt, AngryBird và Nemo.
- Abstract class Machine và các sub class: McQueen, Siddeley.
Như bạn thấy, mặc dù cả McQueen và Bolt đều được hiện thực interface
Runable, nhưng chúng hoàn toàn thuộc hai loại khác nhau. Và tất nhiên
một class có thể can-do nhiều thứ, ví dụ như Bolt có thể chạy và “bow wow”.
Dùng Interface như một “bản thiết kế” của class?
Đây là một điều thường được dùng để trả lời cho hai câu hỏi:
- Interface được dùng để làm gì?
- Tại sao không thể định nghĩa phần thân cho các phương thức của interface.
Xét ở một mức độ nào đó điều này là hợp lý, nhưng như đã nói ở phần
trên, nó chỉ được dùng để mô tả một “bản thiết kế” cho một chức năng của
class. Nếu muốn tạo một bản thiết kế, hãy sử dụng abstract class. Một
bản thiết kế tất nhiên sẽ có những thứ đã được dựng sẵn và có những thứ
là abstract.
Một câu trả lời có thể lý giải phần nào câu hỏi thứ hai là việc cho
phép định nghĩa thân phương thức trong các interface có thể khiến cho
hiệu suất bị giảm sút. Nguyên nhân là việc tìm kiếm các phương thức sẽ
diễn ra lâu hơn vì phải duyệt qua các interface, thay vì chỉ cần phải
tìm trong class cha của nó.
Về công dụng của interface, xét ở mức ứng dụng, các interface có thể
được hiểu như các plugin hoặc thư viện/phần mềm third-party. Việc hiện
thực một interface cho class cũng giống như cài thêm plugin cho một phần
mềm vậy.
Bảng so sánh
Cuối cùng, cũng nên liệt kê các điểm khác biệt giữa hai khái niệm này
để bạn có thể sử dụng được khi cần thiết. Các điểm khác biệt này có thể
khác nhau tùy vào ngôn ngữ mà bạn sử dụng. Vì vậy bạn chỉ cần nhớ các
điểm căn bản sau:
Interface
Abstract class
Multiple inheritance
Một class có thể hiện thực nhiều interface.(tạm coi là thừa kế)
Không hỗ trợ đa thừa kế
Default implementation
Không thể định nghĩa code xử lý, chỉ có thể khai báo.
Có thể định nghĩa thân phương thức, property.
Access Modifiers
Mọi phương thức, property đều mặc định là public.
Có thể xác định modifier.
Adding functionality
Mọi phương thức, property của interface cần được hiện thực trong class.
-- =============================================
-- Author:
-- Create date: <20090123>
-- Description:
-- =============================================
CREATE FUNCTION dbo.f_BinaryToBase64
(
@bin VARBINARY(MAX)
)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE @Base64 VARCHAR(MAX)
/*
SELECT dbo.f_BinaryToBase64(CONVERT(VARBINARY(MAX), 'Converting this text to Base64...'))
*/
SET @Base64 = CAST(N'' AS XML).value('xs:base64Binary(xs:hexBinary(sql:variable("@bin")))', 'VARCHAR(MAX)')
RETURN @Base64
END
-- =============================================
-- Author:
-- Create date: <20090123>
-- Description:
-- =============================================
CREATE FUNCTION dbo.f_Base64ToBinary
(
@Base64 VARCHAR(MAX)
)
RETURNS VARBINARY(MAX)
AS
BEGIN
DECLARE @Bin VARBINARY(MAX)
/*
SELECT CONVERT(VARCHAR(MAX), dbo.f_Base64ToBinary('Q29udmVydGluZyB0aGlzIHRleHQgdG8gQmFzZTY0Li4u'))
*/
SET @Bin = CAST(N'' AS XML).value('xs:base64Binary(sql:variable("@Base64"))', 'VARBINARY(MAX)')
RETURN @Bin
END
20090123>20090123>
-- =============================================
-- VARCHAR
-- =============================================
DECLARE @Base64Data VARCHAR(MAX)
SET @Base64Data = dbo.f_BinaryToBase64(CONVERT(VARBINARY(MAX),'testing...'))
SELECT @Base64Data AS Base64, CONVERT(VARCHAR(MAX), dbo.f_Base64ToBinary(@Base64Data)) AS My_Data
-- =============================================
-- BIGINT
-- =============================================
DECLARE @BigIntData BIGINT
DECLARE @Base64Data VARCHAR(MAX)
SET @BigIntData = 1242353245346345643
SET @Base64Data = dbo.f_BinaryToBase64(CONVERT(VARBINARY(MAX), @BigIntData))
SELECT @Base64Data AS Base64, CONVERT(BIGINT, dbo.f_Base64ToBinary(@Base64Data)) AS [BigInt]
-- =============================================
-- DATETIME
-- =============================================
DECLARE @myDate DATETIME
DECLARE @Base64Data VARCHAR(MAX)
SET @myDate = GETDATE()
SET @Base64Data = dbo.f_BinaryToBase64(CONVERT(VARBINARY(MAX), @myDate))
SELECT @Base64Data AS Base64, CONVERT(DATETIME, dbo.f_Base64ToBinary(@Base64Data)) AS myDate
-- =============================================
-- XML
-- =============================================
DECLARE @XML XML
DECLARE @Base64Data VARCHAR(MAX)
SET @XML = (
SELECT TABLE_NAME, ORDINAL_POSITION, COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS AS row
WHERE TABLE_NAME = (SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME NOT LIKE 'sys%')
FOR XML AUTO, ELEMENTS, ROOT('root')
)
SET @Base64Data = dbo.f_BinaryToBase64(CONVERT(VARBINARY(MAX), @XML))
SELECT @Base64Data AS [Base64]
DECLARE @New_XML XML
SET @New_XML = CONVERT(XML, dbo.f_Base64ToBinary(@Base64Data))
SELECT row.col.value('./TABLE_NAME[1]','NVARCHAR(128)') AS TABLE_NAME
, row.col.value('./ORDINAL_POSITION[1]','INT') AS ORDINAL_POSITION
, row.col.value('./COLUMN_NAME[1]','NVARCHAR(128)') AS COLUMN_NAME
FROM @New_XML.nodes('//row') AS row(col)