Chương 6 – Sửa đổi DOM
Bạn đã từng xem ảo thuật và thấy những ảo thuật gia có thể với tay lên không trung và cho
xuất hiện một bó hoa, jQuery cũng có thể tạo ra các thành phần, thuộc tính, và cả chữ trên
một trang web giống với cách mà ảo thuật gia trình diễn vậy. Hơn nữa, jQuery cũng có thể
làm biến mất tất cả những thứ nó tạo ra. Và chúng ta cũng có thể lấy bó hoa kia và biến nó
thành <div class=’magic’ id=’flowerToDove’>Dove</div>
Sửa đổi thuộc tính
Qua 4 chương đầu của giáo trình này, chúng ta đã biết cách sử dụng phương thức .addClass()
và .removeClass() để làm thay đổi giao diện của các thành phần trên trang web. Thực chất thì
những phương thức này sửa đổi thuộc tính của class. Phương thức .addClass() thì tạo ra hoặc
thêm vào cho thuộc tính, trong khi phương thức .removeClass() thì lại xoá hoặc giảm thuộc
tính. Còn có một phương thức nữa là .toggleClass(), nó có thể vừa loại bỏ và vừa thêm vào
một class. Như thế với 3 phương thức trên chúng ta đã có những công cụ đủ mạnh để làm
việc với class.
Tuy nhiên, thuộc tính class chỉ là một trong số những thuộc tính mà chúng ta cần dùng tới
hoặc thay đổi. Ví dụ, id, rel và href. Để sửa đổi những thuộc tính này, jQuery cung cấp
phương thức .attr() và .removeAttr(). Chúng ta thậm chí có thể sử dụng .attr() và
.removeAttr() để sửa đổi thuộc tính class. Nhưng phương thức chuyên dụng là .addClass() và
.removeClass() thì phù hợp hơn trong trường hợp này bởi vì nó có thể xử lý chính xác những
trường hợp một phần tử có nhiều class như: <div class=’first second’>.
Thuộc tính phi class
Có những thuộc tính không đơn giản để sửa đổi nếu không có sự trợ giúp của jQuery. Hơn
nữa, jQuery cho phép chúng ta sửa đổi nhiều thuộc tính cùng một lúc, tương tự như cách mà
chúng ta làm việc với nhiều thuộc tính CSS khi sử dụng phương thức .css() ở chương 4.
Ở ví dụ này, chúng ta có thể dễ dàng thiết lập id, rel và thuộc tính title cho đường liên kết
cùng một lúc. Dưới đây là mã HTML
<h1>jQuery DOM Manipulation</h1>
<h3>An example at izwebz.com</h3>
<div class="chapter">
<p class="first">Qua 4 chương đầu của giáo trình này, chúng ta đã
biết cách sử
dụng phương thức .addClass() và .removeClass() để làm thay đổi giao
diện của
các thành phần trên trang web. <a href="http://www.izwebz.com">Thực</span>
chất</a> thì những phương thức này sửa đổi thuộc tính của class. Phương
thức .addClass() thì tạo ra hoặc thêm vào cho
thuộc tính, trong khi phương thức .removeClass() thì lại xoá hoặc
giảm thuộc
tính.
</p>
<p class="second method">Tuy nhiên, thuộc tính class chỉ là một
trong số
những thuộc tính mà chúng ta cần dùng tới hoặc thay đổi. Ví dụ, id,
rel và href.
Để sửa đổi những thuộc tính này, <a
href="http://www.izwebz.com">jQuery</a> cung cấp phương thức .attr()
và .removeAttr(). Chúng ta thậm chí có thể sử dụng .attr() và
.removeAttr()
để sửa đổi <a href="http://www.izwebz.com">thuộc tính</a> class.
Nhưng phương thức chuyên dụng là .addClass()
và .removeClass() thì phù hợp hơn trong trường hợp này bởi vì nó
<a href="http://www.izwebz.com">có thể</a> xử lý
chính xác những trường hợp một phần tử có nhiều class như:
</p>
<p><span class="pull-quote">
Có những thuộc tính <span class="drop">không đơn giản</span> để sửa
đổi nếu không có sự trợ giúp của
jQuery. Hơn nữa, <strong>jQuery</strong> cho phép chúng ta sửa đổi
nhiều thuộc tính cùng một lúc,
tương tự như cách mà chúng ta làm việc với nhiều thuộc tính
CSS</span> khi sử dụng
phương thức .css() ở chương 4. Ở ví dụ này, chúng ta có thể dễ
dàng thiết lập
id, rel và thuộc tính title cho đường liên kết cùng một lúc. Dưới
đây là mã
HTML
</p>
</div><!–End .chapter–>
Bây giờ chúng ta có thể đi qua từng đường liên kết trong thẻ <div class=’chapter’> và áp
dụng thuộc tính cho chúng từng thẻ một. Nếu bạnchỉ muốn tạo ra một giá trị thuộc tính giống
nhau cho tất cả các đường liên kết, thì bạn chỉ cần một dòng mã đơn giản sau:
$(document).ready(function() {
$('div.chapter a').attr({'rel': 'external'});
});
Cách này có thể dùng được bởi vì chúng ta muốn giá trị của thuộc tính rel vừa tạo là như
nhau ở tất cả các đường liên kết. Tuy nhiên, thường thì những thuộc tính ta thêm vào hoặc
thay đổi phải có giá trị khác nhau cho mỗi một thành phần. Ví dụ với bất cứ tài liệu nào, mỗi
một id đều phải là duy nhất nếu ta muốn mã javaScript của mình làm việc theo ý muốn. Để
tạo được một id duy nhất cho mỗi đường liên kết, chúng ta không sử dụng phương pháp ở
trên nữa mà thay vào đó sử dụng phương thức .each().
$(document).ready(function() {
$('div.chapter a').each(function(index) {
$(this).attr({
'rel': 'external',
'id': 'izwebz-' + index
});
});
});
Phương thức .each() hoạt động như vòng lặp hiện, nó có nguyên lý hoạt động như vòng lặp
for nhưng thuận tiện hơn. Người ta thường sử dụng phương thức này khi mà đoạn mã chúng
ta sử dụng trên mỗi phần tử của bộ chọn quá phức tạp cho vòng lặp ẩn. Trong trường hợp
này, hàm ẩn của phương thức .each() được gán một số index để chúng ta có thể gắn nó cho
mỗi id. Đối số index này hoạt động như một bộ đếm, bắt đầu từ số 0 cho đường liên kết đầu
tiên và tăng dần 1 đơn vị cho mỗi đường liên kết kế tiếp. Cho nên khi ta thiết lập id thành
‘izwebz-’ + index, thì đường liên kết đầu tiên sẽ có id là izwebz-0, đường liên kết thứ 2 sẽ
làizwebz -1, v.v..
Xem Demo Online – Example 1 (dùng firebug để inspect link)
Chúng ta sẽ sử dụng thuộc tính title để cho người đọc biết thêm thông tin về đường liên kết ở
Izwebz. Ở ví dụ dưới đây, tất cả các đường liên kết đều hướng tới izwebz.com. Tuy nhiên,
chúng ta nên để cho biểu thức bộ chọn được cụ thể hơn, chúng ta chỉ nên chọn những đường
liên kết có chứa izwebz trong phần href. Để phòng sau này chúng ta lại thêm những đường
liên kết khác không phải là izwebz.
$(document).ready(function() {
$('div.chapter a[href*=izwebz]').each(function(index) {
var $thisLink = $(this);
$thisLink.attr({
'id': 'izwebzLink-' + index,
'title': 'know more about ' + $thisLink.text() + ' at
izwebz'
});
});
});
Ở đây có điểm bạn cần chú ý là chúng ta đã lưu lại từ khoá $(this) vào một biến gọi là
$thisLink, bởi vì chúng ta sử dụng nó nhiều hơn một lần.
Với cả 3 giá trị thuộc tính được thiết lập như trên, bây giờ đường liên kết của chúng ta sẽ có
dạng như sau:
<a href="http://www.izwebz.com"</span> rel="external" id="izwebzLink-0"
title="know more about Thực chất at izwebz">Thực chất</a>
Xem Demo Online – Example 2 (dùng firebug để inspect link)
Ôn lại hàm $()
Từ khi bắt đầu làm quen với jQuery, chúng ta đã biết cách sử dụng hàm $() để tiếp cận các
thành phần trên trang. Thực tế thì hàm này là trọng tâm của thư viện jQuery, bởi vì nó được
gọi mỗi khi chúng ta cần gán một hiệu ứng, sự kiện hoặc thuộc tính cho một phần tử.
Nhưng hàm $() còn một chức năng khác nữa nằm trong hai dấu ngoặc – tính năng này rất đỗi
mạnh mẽ đến nỗi nó không những có thể thay đổi giao diện của một thành phần mà nó còn có
thể thay đổi nội dung của một trang web. Chỉ đơn giản bằng cách chèn một đoạn mã HTML
nằm giữa hai dấu ngoặc, chúng ta có thể tạo ra một cấu trúc DOM mới từ hư vô.
Chú ý: Bạn cũng nên chú ý khi tạo ra những hiệu ứng để cải thiện giao diện hoặc nội dung
phụ thuộc vào javaScript. Bởi vì không phải ai cũng bật javaScript, nên những thông tin quan
trọng phải được nhìn thấy bởi tất cả mọi người, chứ không phải chỉ nhóm người có trình
duyệt hiện đại hoặc bật javaScript.
Một chức năng thường thấy trong những trang FAQs là đường liên kết Back to top ở dưới
mỗi câu hỏi và trả lời. Bởi vì cái này nếu có bỏ đi hoặc không được hiển thị ở một số trình
duyệt thì cũng không ảnh hưởng đến nội dung chính của trang. Do vậy chúng ta có thể dùng
JavaScript để thêm vào. Chúng ta sẽ thêm vào đường liên kết Back to top ở cuối mỗi đoạn
văn, và điểm dừng mà đường liên kết Back to top sẽ dẫn tới. Chúng ta tạo ra các thành phần
mới như sau:
$(document).ready(function() {
$('<a href="#top">back to top</a>');
$('<a id="top"></a>');
});
Khi cho chạy thử đoạn mã trên, bạn vẫn không thấy những đường liên kết back to top và các
điểm dừng xuất hiện, cho dù ta đã tạo nó ở đoạn code trên. Vấn đề là dòng mã ở trên đã tạo ra
các thành phần ta muốn, nhưng nó chưa được thêm vào trang. Để làm được điều này, chúng
ta có thể sử dụng một trong rất nhiều phương thức chèn của jQuery.
jQuery có hai phương thức dùng để chèn phần tử này vào trước phần tử kia là: .insertBefore()
và .before(). Hai phương thức này có cùng chức năng, nhưng khác nhau ở điểm là nó sẽ được
kết hợp với các phương thức khác như thế nào. Còn hai phương pháp nữa là, .insertAfter() và
.after(), cũng có nguyên lý hoạt động như nhau nhưng nó được sử dụng để chèn phần tử này
vào sau phần tử kia. Với ví dụ về back to top của ta, chúng ta sẽ sử dụng phương pháp
.insertAfter().
$(document).ready(function() {
$('<a href="#top">back</span> to top</a>')
.insertAfter('div.chapter p');
$('<a id="top"></a>');
});
Phương thức .after() cũng có thể cho kết quả tương tự với .insertAfter(), nhưng với biểu thức
bộ chọn nằm trước phương thức thay vì theo sau nó. Nếu sử dụng .after(), thì dòng mã đầu
tiên trong $(document).ready() sẽ là như sau:
$('div.chapter').after('<a href='#top'>back to top</a>');
Với .insertAfter() thì bạn vẫn có thể thêm vào đằng sau nó những phương thức khác để tiếp
tục làm việc với thẻ <a’>. Nhưng với .after(), những phương thức bạn thêm vào sau này sẽ
chỉ có tác dụng với những phần tử phù hợp với bộ chọn – trong trường hợp này là –
$(‘div.chapter p’). Nói cách khác, thẻ <a’> của bạn sẽ không chịu ảnh hưởng bởi những
phương thức thêm vào sau nó.
Bây giờ chúng ta đã chính thức chèn đường liên kết vào trang web (và vào trong DOM) sau
mỗi một đoạn văn nằm trong thẻ <div class=’chapter’>, đường liên kết back to top sẽ xuất
hiện như hình.
Nhưng hiện tại những đường liên kết vẫn chưa hoạt động được. Chúng ta vẫn còn phải chèn
điểm dừng với id=’top’. Chúng ta có thể sử dụng một trong những phướng thức dùng để chèn
một phần tử vào một phần tử khác.
$(document).ready(function() {
$('<a href="#top">back</span> to top</a>')
.insertAfter('div.chapter p');
$('<a id="top" name="top"></a>')
.prependTo('body');
});
Đoạn mã trên chèn điểm dừng ngay trên phần bắt đầu của thẻ <body>, hay nói cách khác là
trên cùng của trang. Với phương thức .insertAfter() cho đường liên kết và .prependTo() được
sử dụng cho điểm dừng, những đường liên kết back to top của chúng ta đã hoạt động được.
Một điểm thường thấy nữa của những đường liên kết back to top là nó không có tác dụng gì
khi nằm trên cùng của trang vì phần đầu người đọc vẫn nhìn thấy được. Chúng ta cần chỉnh
sửa lại mã một chút sao cho những đường liên kết chỉ bắt đầu sau đoạn văn thứ 4. Để đạt
được điều này, chúng ta chỉ cần thay đổi biểu thức bộ chọn một chút:
.insertAfter(‘div.chapter:gt(2)’). Tại sao lại có giá trị là 2 ở đây? Bởi vì JavaScript đánh số
bắt đầu từ 0, cho nên đoạn văn đầu tiên sẽ là số 0, đoạn văn thứ 2 là số1, thứ 3 là số 2 và thứ
tư là số 3. Biểu thức bộ chọn của chúng ta sẽ chèn đường liên kết vào sau mỗi đoạn văn khi
Hình dưới đây cho bạn thấy kết quả của biểu thức bộ chọn ở trên.
Xem Demo Online – Example 3
Di chuyển các phần tử
Với ví dụ về đường liên kết back to top ở trên, chúng ta đã tạo ra những phần tử mới và chèn
chúng vào trang. Nhưng chúng ta cũng có thể di chuyển một phần tử từ nơi này qua nơi khác.
Một ứng dụng thực tế của cách chèn này là dạng tự động hoá cách chèn phần ghi chú ở cuối
trang. Một phần chú thích đã có trong đoạn văn mẫu và chúng ta sẽ sử dụng nó trong ví dụ
này. Chúng ta cũng đã tạo ra một số phần ghi chú khác cho ví dụ này.
<p><span class="pull-quote">
Có những thuộc tính <span class="drop">không đơn giản</span> để sửa
đổi nếu không có sự trợ giúp của
jQuery. Hơn nữa, <strong>jQuery</strong> cho phép chúng ta</span>
sửa đổi nhiều thuộc tính cùng một lúc,
tương tự như cách mà chúng ta làm việc với nhiều thuộc tính CSS
khi sử dụng
phương thức .css() ở chương 4. Ở ví dụ này, chúng ta có thể dễ
dàng thiết lập
id, rel và thuộc tính title cho đường liên kết cùng một lúc. Dưới
đây là mã
HTML
</p>
<p>
Pellentesque habitant morbi tristique senectus et netus et
malesuada fames
ac turpis egestas. <span class="footnote">Vestibulum tortor quam,
feugiat vitae, ultricies eget,
tempor sit amet, ante. Donec eu libero sit amet quam egestas
semper.
Aenean ultricies mi vitae est</span>. Mauris placerat eleifend
leo. Quisque
sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi,
condimentum sed, commodo vitae, ornare sit amet, wisi.
</p>
<p>
Pellentesque habitant morbi tristique senectus et netus et
malesuada fames
ac turpis egestas. Vestibulum tortor quam, feugiat vitae,
ultricies eget,
tempor sit amet, ante. <span class="footnote">Donec eu libero sit
amet quam egestas semper.
Aenean ultricies mi vitae est. Mauris placerat eleifend leo.
Quisque
sit amet est et sapien ullamcorper pharetra.</span> Vestibulum
erat wisi,
condimentum sed, commodo vitae, ornare sit amet, wisi.
</p>
<p>
Pellentesque habitant morbi tristique senectus et netus et
malesuada fames
ac turpis egestas. Vestibulum tortor quam, feugiat vitae,
ultricies eget,
tempor sit amet, ante. Donec eu libero sit amet quam egestas
semper.
Aenean ultricies mi vitae est. <span class="footnote">Mauris
placerat eleifend leo. Quisque
sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi,
condimentum sed, commodo vitae, ornare sit amet, wisi.</span>
</p>
Mỗi một đoạn văn này có một đoạn ghi chú được gói trong thẻ <span
class=’footnote’></span>. Khi viết mã HTML như thế này, chúng ta đã bảo tồn được ngữ
đây.
Bây giờ chúng ta có thể lấy phần ghi chú và chèn chúng vào giữa hai thẻ <div
class=’chapter’> và <div id=’footer’>. Bạn cũng nên biết rằng kể cả trong trường hợp của
vòng lặp ẩn, thứ tự chèn đã được định trước, bắt đầu từ trên cây DOM đi xuống dưới. Bởi vì
chúng ta cũng cần bảo tồn thứ tự của phần ghi chú ở vị trí mới của nó trên trang, cho nên
chúng ta sẽ dùng .insertBefore(‘#footer’).
Làm như vậy sẽ chèn từng phần ghi chú ngay trên thẻ <div id=’footer’>. Do đó ghi chú thứ
nhất sẽ được đặt nằm giữa <div class=’chapter’> và <div id=’footer’>, ghi chú thứ 2 sẽ được
nằm giữa ghi chú thứ nhất và thẻ <div id=’footer’>, v.v.. Mặt khác, nếu chúng ta sử dụng
.insertAfter(‘div.chapter’), thì thứ tự sẽ bị đảo lộn. Cho nên mã của chúng ta sẽ như sau
$(document).ready(function() {
$('span.footnote').insertBefore('#footer');
});
Ở đây chúng ta lại có thêm một vấn đề nữa. Đó là các dòng ghi chú được nằm trong thẻ
<span>, mà bản thân thẻ <span> có display: inline theo mặc định, do vậy các dòng ghi chú
nối liền nhau mà không xuống dòng.
Chúng ta sẽ chỉnh sửa CSS để giải quyết vấn đề này, chúng ta sẽ làm cho những phần tử
<span> trở thành block-level, nhưng chỉ áp dụng với những thẻ nằm trong <div
class=’chapter’>
span.footnote {
font-style: italic;
font-family: "Times New Roman", Times, serif;
display: block;
margin: 1em 0;
}
.chapter span.footnote {
display: inline;
}
Bây giờ thì phần ghi chú của chúng ta đã xuống dòng.
Xem Demo Online – Example 4
Ít ra thì phần ghi chú của chúng ta nhìn cũng đã tạm được rồi, nhưng còn nhiều việc chúng ta
có thể làm để cải thiện nó. Một số điểm cần làm như sau:
1.Đánh dấu vị trí trong tài liệu nơi mà ghi chú được sử dụng
2.Đánh số cho từng vị trí và cung cấp một số phù hợp với bản thân từng dòng ghi chú.
3.Tạo đường liên kết giữa vị trí văn bản đến điểm ghi chú, và từ điểm ghi chú ngược lại đoạn
văn bản.
Những bước này có thể làm được nhờ phương thức .each(), nhưng trước hết chúng ta phải tạo
ra một nơi chứa những dòng ghi chú ở dưới cùng của trang.
$(document).ready(function() {
$('<ol id="notes"></ol>').insertAfter('div.chapter');
});
Chúng ta sử dụng một danh sách đánh số <ol id=’notes’> </ol> cho phần ghi chú, bởi vì
mình muốn chúng tự động được đánh số thứ tự. Chúng ta đã cho danh sách này một
id=’notes’ và chèn nó vào sau thẻ <div class=’chapter’>.
Đánh dấu, đánh số và liên kết văn bản
Bây giờ chúng ta đã có thể đánh dấu và đánh số vị trí mà phần ghi chú được trích dẫn.
$(document).ready(function() {
$('<ol id="notes"></ol>').insertAfter('div.chapter');
$('span.footnote').each(function(index) {
$(this)
.before(
['<a href="#foot-note-',</span>
index+1,
'" id="context-',
index+1,
'" class="context">',
'<sup>' + (index+1) + '</sup>',
'</a>'
].join('')
)
});
});
Ở đây chúng ta sử dụng bộ chọn giống với bộ chọn ở ví dụ trước, nhưng khác cái là chúng ta
gắn phương thức .each() cho nó. Ở trong .each() chúng ta bắt đầu bằng $(this), nó là đại diện
của từng dòng ghi chú liên tiếp nhau, và chúng ta gắn phương thức .method() cho nó.
Kết quả của cả đoạn mã “loằng ngoằng” nằm trong dấu ngoặc đơn của phương thức .before()
sẽ là một đường siêu liên kết. Nó sẽ được chèn vào trước từng thẻ <span> của phần ghi chú.
Dưới đây là mã HTML của một trong những dòng ghi chú khi nó được chèn vào DOM.
<a href="#foot-note-1" id="context-1"
class="context"><sup>1</sup></a>
Cú pháp nhìn có vẻ khá phức tạp, nên chúng ta dành vài phút để tìm hiểu xem nó như thế
nào. Bên trong dấu ngoặc đơn của phương thức .before(), chúng ta bắt đầu với một cặp ngoặc
vuông -[ ] – nó chính là đại diện của mảng trực kiện (array literal). Mỗi phần tử nằm trong
array sẽ có một dấu phảy theo sau (trừ phần tử cuối cùng, nếu không mã sẽ không chạy). Để
cho dễ đọc hơn, chúng ta đã viết mỗi lệnh trên một dòng. Sau khi mảng đã lập xong, chúng ta
Phương thức này lấy vào một chuỗi rỗng làm đối số, chuỗi rỗng được biểu thị bởi một cặp
dấu nháy (‘ ‘), bởi vì chúng ta không muốn bất cứ thứ gì xuất hiện giữa các phần tử mảng khi
nó được xuất ra dạng HTML.
Lưu ý bạn đến phần index+1 trên đoạn mã trên. Bởi vì JavaScript đánh số bắt đầu từ 0, do
vậy để thuộc tích href có giá trị là #footnote-1, thì chúng ta phải công 1 đơn vị cho nó. Thuộc
tính id là #context-1 và tên của đường link sẽ là 1. Thuộc tính href đặc biệt quan trọng, bởi vì
nó phải tuyệt đối phù hợp với thuộc tính id của phần ghi chú (tất nhiên là không gồm dấu #).
Cách thứ 2 chúng ta cũng có thể sử dụng chuỗi nối thay vì dùng mảng ghép:
.before('<a href="#foot-note-'</span> + (index+1) +
'" id="context-' + (index+1) +
'" class="context"><sup>' +
(index+1) + '</sup></a>');
Nhưng trong trường hợp này, cách sử dụng mảng có vẻ dễ quản lý hơn.
Lưu ý: Đã có nhiều tài liệu nói về sự khác nhau về mặt hiệu năng làm việc giữa mảng ghép
và chuỗi nối. Nếu bạn còn phân vân, bạn có thể đọc tài liệu sau
http://www.sitepen.com/blog/2008/05/09/string-performance-an-analysis/
Tuy nhiên, trong hầu hết các trường hợp, những khác biệt này là không đáng kể. Nếu hiệu
năng làm việc của đoạn mã là điều cần quan tâm thì còn nhiều yếu tố khác có tầm ảnh hưởng
còn lớn hơn như là cách lưu bộ chọn mà chúng ta đã bàn.
Xem Demo Online – example 5
Gắn phần ghi chú
Bước kế tiếp là di chuyển phần tử <span class=’footnote’> như chúng ta đã làm. Tuy nhiên,
lần này chúng ta sẽ đặt nó vào trong thẻ <ol id=’note’> vừa mới được tạo. Chúng ta sẽ sử
dụng .appendTo(), nhưng cũng để giữ đúng thứ tự, những dòng ghi chú kế tiếp sẽ được chèn
vào cuối của hàng.
$('<ol id="notes"></ol>').insertAfter('div.chapter');
$('span.footnote').each(function(index) {
$(this)
.before(
['<a href="#foot-note-',</span>
index+1,
'" id="context-',
index+1,
'" class="context">',
'<sup>' + (index+1) + '</sup>',
'</a>'
].join('')
)
.appendTo('#notes')
});
});
Bạn cũng nên nhớ rằng .appendTo() đang được gắn với $(this), cho nên nói theo ngôn ngữ
của jQuery thì là “gắn phần ghi chú span vào phần tử với id là ‘notes’.
Đối với mỗi dòng ghi chú mà chúng ta vừa di chuyển, chúng ta sẽ gắn cho nó một đường liên
kết khác. Đường liên kết này sẽ quay lại số thứ tự nằm trong văn bản.
$(document).ready(function() {
$('<ol id="notes"></ol>').insertAfter('div.chapter');
$('span.footnote').each(function(index) {
$(this)
.before(
['<a href="#foot-note-',
index+1,
'" id="context-',
index+1,
'" class="context">',
'<sup>' + (index+1) + '</sup>',
'</a>'
].join('')
)
.appendTo('#notes')
.append( ' (<a href="#context-' + (index+1) +
'">context</a>)' );
});
});
Chú ý đến thuộc tính href quay ngược lại id phù hợp với chỗ đánh dấu trước. Dưới đây bạn sẽ
thấy phần ghi chú đã được gắn đường liên kết.
bởi vì mỗi một dòng sẽ phải được đặt nằm giữa thẻ <li>.
Gói phần tử
Phương thức dùng để gói một phần tử này bên trong phần tử khác có tên là .wrap(). Bởi vì
chúng ta muốn mỗi một $(this) sẽ được gói trong cặp thẻ <lli><l/li>, chúng ta có thể hoàn
thiện mã cho phần ghi chú như sau:
$(document).ready(function() {
$('<ol id="notes"></ol>').insertAfter('div.chapter');
$('span.footnote').each(function(index) {
$(this)
.before(
['<a href="#foot-note-',</span>
index+1,
'" id="context-',
index+1,
'" class="context">',
'<sup>' + (index+1) + '</sup>',
'</a>'
].join('')
)
.appendTo('#notes')
.append( ' (<a href="#context-' + (index+1) +
'">context</a>)' )
.wrap('<li id="foot-note-' + (index+1) +
'"></li>');
});
});
Bây giờ mỗi phần tử <li> đều có một id bằng với href của nó. Cuối cùng chúng ta đã có một
danh sách các đoạn ghi chú được đánh dấu và liên kết với nhau.
Xem Demo Online – Example 6
Tất nhiên, số thứ tự có thể được chèn vào trước từng dòng ghi chú như cách mà chúng được
chèn trong đoạn văn. Tuy nhiên bạn có thể thấy rất “khoái chí” khi chúng ta có những đoạn
mã hợp chuẩn được tự động tạo ra bởi JavaScript.
Từ đầu chương cho tới giờ chúng ta đã chèn phần tử được tạo ra, di chuyển nó từ vị trí này
sang vị trí khác của tài liệu, gói phần tử đã có bằng một phần tử mới. Nhưng cũng có khi
chúng ta muốn sao chép một phần tử nào đó. Ví dụ khi bạn có một thanh di chuyển ở trên
cùng của trang, bạn muốn copy nó và chèn nó xuống phần footer. Thực ra nếu bạn muốn
copy lại phần nào của trang để tăng tính thẩm mỹ của nó thì bạn nên sử dụng mã để cho nó
làm tự động cho mình. Mà suy cho cùng thì tại sao chúng ta phải mất thời gian và công sức
để viết lại một đoạn mã y chang làm gì? Tại sao không để jQuery làm giúp vừa lẹ mà lại vừa
tránh sai sót.
Để sao chép phần tử, phương thức .clone() là tất cả những gì chúng ta cần. Phương thức này
sẽ tạo ra một bản sao của phần tử mà chúng ta chọn để dùng sau này. Cũng như những
phương thức tạo ra các phần tử mới mà chúng ta đã học ở trên, phương thức này cũng không
tạo ra thay đổi gì cho đến khi chúng ta sử dụng một trong những phương thức chèn. Cũng
giống như khi bạn copy một đoạn văn trong Microsoft Word, khi bạn vừa bôi đen và chọn
copy. Thì MS Word sẽ lưu đoạn văn đó trong bộ nhớ của máy, nhưng trên tài liệu chưa có gì
xuất hiện. Cho đến khi bạn chọn Paste thì đoạn văn copy mới được hiển thị. Phương thức
.clone() cũng hoạt động theo nguyên lý đó.
Đoạn mã dưới đây sẽ tạo ra một bản sao của đoạn văn nằm trong thẻ <div class=’chapter’>.
$('div.chapter p:eq(0)').clone();
Như đã nói ở trên, khi bạn cho chạy đoạn code trên sẽ không có gì xảy ra hết. Bởi vì jQuery
mới chỉ copy đoạn văn đó thôi chứ nó chưa làm gì với nó cả.
Để cho đoạn văn chúng ta vừa copy xuất hiện, chúng ta sẽ cho nó xuất hiện ở trên thẻ <div
class=’chapter’>
$('div.chapter p:eq(0)').clone().insertBefore('div.chapter');
Bây giờ nếu cho chạy đoạn mã trên, bạn sẽ thấy đoạn văn đầu được xuất hiện 2 lần và bởi vì
nó không còn nằm trong thẻ <div class=’chapter’> nữa cho nên những style nào bạn áp dụng
trong CSS sẽ không có tác dụng với nó. Ở đây bạn thấy rõ nhất là nó rộng hơn những đoạn
văn còn lại.
Sao chép kèm sự kiện
Mặc định của phương thức .clone() là không sao chép bất cứ sự kiện nào hoặc “họ hàng nội
ngoại” của đối tượng được chọn. Nhưng nó cũng có thể lấy vào một tham số Boolean, mà khi
giá trị này là true, nó sẽ sao chép cả sự kiện đi kèm: .clone(true). Như vậy chúng ta đỡ phải
mất công gán lại sự kiện cho nó trong trường hợp chúng ta muốn nó đi kèm.
Sao chép cho phần trích dẫn
Khi bạn đọc báo giấy, đôi khi bạn thấy có phần khung in nhỏ, trong đó có những mẩu trích
dẫn những đoạn quan trọng để gây sự chú ý. Chúng ta có thể làm được việc này bằng cách sử
dụng phương thức .clone(). Hãy xem lại đoạn mã HTML của đoạn văn thứ 3 của chúng ta.
<span class="pull-quote">It is a Law of Nature
<span class="drop">with us</span> that a male child shall
have <strong>one more side</strong> than his father</span>
so that each generation shall rise (as a rule) one step in
the scale of development and nobility. Thus the son of a
Square is a Pentagon; the son of a Pentagon, a Hexagon; and
so on.
</p>
Đoạn văn bắt đầu bằng một thẻ <span class=’pull-quote’>. Đây sẽ là class chúng ta sẽ dùng
để sao chép. Một khi đoạn văn nằm trong thẻ <span> đó được copy, chúng ta sẽ dán nó vào
một nơi khác và chúng ta cũng cần chỉnh sửa lại style cho nó bắt mắt hơn.
Chúng ta sẽ gán một class là ‘pulled’ cho thẻ <span> vừa được copy và khai báo những thuộc
tính sau trong CSS
.pulled {
background: #e5e5e5;
position: absolute;
width: 145px;
top: -20px;
right: -180px;
padding: 12px 5px 12px 10px;
font: italic 1.4em "Times New Roman", Times, serif;
}
Đoạn trích dẫn có màu nền xanh nhạt, một ít padding và font khác. Chúng ta cũng sử dụng
absolute position để định vị cho thành phần mới này.
Bây giờ chúng ta sẽ quay lại phần jQuery. Chúng ta sẽ bắt đầu với biểu thức bộ trọng cho tất
cả các thẻ <span class=’pull-quote’>, sau đó chúng ta sẽ gán vào phương thức .each() để chạy
qua từng phần tử một.
$(document).ready(function() {
$('span.pull-quote').each(function(index) {
//…
});
});
Tiếp theo chúng ta tìm đoạn văn “cha mẹ” của từng đoạn trích dẫn và áp dụng
thuộc tính CSS
$(document).ready(function() {
$('span.pull-quote').each(function(index) {
var $parentParagraph = $(this).parent('p');
$parentParagraph.css('position', 'relative');
});
});
Một lần nữa, chúng ta lưu lại bộ chọn mà chúng ta sẽ sử dụng hơn một lần vào một biến để
giúp cho mã của chúng ta hoạt động hiệu quả hơn và cũng dễ đọc hơn.
Bây giờ sau khi đã thiết lập xong hai giá trị định vị quan trọng của CSS là relative cho đoạn
văn chứa phần trích dẫn và absolute cho đoạn trích dẫn. Tiếp theo chúng ta có thể copy từng
thẻ <span> và thêm class là pulled cho từng bản sao, và cuối cùng là chèn nó vào phần bắt
đầu của đoạn văn bản.
Lưu ý: nếu bạn chưa hiểu kỹ khái niệm này, bạn có thể xem video về Absolute Position trong
CSS
$(document).ready(function() {
$('span.pull-quote').each(function(index) {
var $parentParagraph = $(this).parent('p');
$parentParagraph.css('position', 'relative');
$(this).clone()
.addClass('pulled')
.prependTo($parentParagraph);
});
});
Bởi vì chúng ta sử dụng absolute position cho đoạn trích dẫn, cho nên nó sẽ định vị theo đoạn
văn bản chứa nó. Dưới đây là hình mà chúng ta có được đến bước này.
Về cơ bản thì đoạn trích dẫn nhìn cũng tạm ổn rồi, nhưng thường thì phần trích dẫn không có
cùng định dạng văn bản như là đoạn văn được copy. Ở đây đoạn chích dẫn của chúng ta có
một vài chữ được tô đậm. Điều chúng ta muốn là đoạn văn sau khi được copy sẽ loại bỏ hết
những thẻ <strong>, <em> và <a href> hoặc bất cứ thẻ inline nào đi. Hơn nữa chúng ta cũng
muốn sau khi copy thì cũng có thể sửa đổi nó chút chút như là xoá đi vài từ và thay thế nó
bằng dấu ba chấm …. Quay lại đoạn mã HTML, bạn sẽ thấy có một vài từ được đặt nằm
trong cặp thẻ <span class=’drop’> không đơn giản </span>.
Chúng ta sẽ làm dấu 3 chấm trước sau đó sẽ loại bỏ hết các thuộc tính HTML đi và chỉ giữ lại
phiên bản chữ không.
$(document).ready(function() {
$('span.pull-quote').each(function(index) {
var $parentParagraph = $(this).parent('p');
$parentParagraph.css('position', 'relative');
var $clonedCopy = $(this).clone();
$clonedCopy
.addClass('pulled')
.find('span.drop')
.html('…')
.end()
.prependTo($parentParagraph);
var clonedText = $clonedCopy.text();
$clonedCopy.html(clonedText);
});
});
Chúng ta bắt đầu quá trình sao chép bằng cách lưu bản sao vào một biến. Lần này biến được
tạo ra là cần thiết bởi vì chúng ta không thể làm việc hoàn toàn với nó trong cùng một dòng
lệnh. Bạn cũng đã thấy là sau khi chúng ta tìm hết những thẻ <span class=’drop’> và thay thế
nó với dấu ba chấm, chúng ta sử dụng một phương thức .end(). Phương thức này nói cho
jQuery biết, chúng ta muốn quay lại một trước bước. Trong trường hợp này chúng ta muốn
quay lại đến bước .find(‘span.drop’). Như thế chúng ta sẽ chèn cả đoạn copy chứ không phải
chỉ là dấu ba chấm vào phần đầu của đoạn văn.
Cuối cùng chúng ta tạo thêm một biến nữa là clonedText, biến này sẽ chứa phiên bản chữ
không của đoạn văn chúng ta cần copy. Sau đó chúng ta sử dụng phiên bản chữ không có
định dạng này để thay thế cho phiên bản HTML. Bây giờ đoạn trích dẫn sẽ như hình
Ở hình trên tôi đã thêm một đoạn <span class=’pull-quote’> vào để xem mã của chúng ta có
thể tự động tạo ra một đoạn trích dẫn nữa không.
Bo tròn góc cho đoạn trích
Bây giờ đoạn trích dẫn đã hoạt động như mình muốn là những phần tử con bị loại bỏ, dấu 3
chấm được thêm vào để thay cho chữ. Nếu bây giờ chúng ta muốn cho đoạn trích dẫn được
bo tròn góc thì sao. Chúng ta có thể tạo ra một thẻ <div> bao quanh lấy đoạn trích.
$(document).ready(function() {
$('span.pull-quote').each(function(index) {
var $parentParagraph = $(this).parent('p');
$parentParagraph.css('position', 'relative');
var $clonedCopy = $(this).clone();
$clonedCopy
.addClass('pulled')
.find('span.drop')
.html('…')
.end()
.prependTo($parentParagraph)
.wrap('<div class="pulled-wrapper"></div>');
var clonedText = $clonedCopy.text();
$clonedCopy.html(clonedText);
});
});
.pulled-wrapper {
background: url(top-bg.png) no-repeat left top;
position: absolute;
width: 160px;
right: -180px;
padding-top: 18px;
}
.pulled {
background: url(bottom-bg.png) no-repeat left bottom;
position: relative;
display: block;
width: 140px;
padding: 0 10px 24px 10px;
font: italic 1.4em "Times New Roman", Times, serif;
}
Xem Demo Online – Example 7
Tóm lược các phương thức sửa đổi DOM
Sự khác biệt giữa những phương thức sửa đổi DOM mà jQuery cung cấp cho chúng ta phụ
thuộc vào nhiệm vụ và vị trí của nó. Phần này sẽ tóm lược cho bạn dễ nhớ phương thức nào
nên được sử dụng để làm gì và khi nào thì nên sử dụng chúng
1.Để tạo một phần tử mới từ HTML, sử dụng hàm $().
2.Để chèn một hoặc nhiều phần tử vào trong một phần tử khác sử dụng
.append()
.appendTo()
.prepend()
.prependTo()
3.Để chèn một phần tử mới vào bên cạnh một phần tử khác sử dụng
.after()
.insertAfter()
.before()
.insertBefore()
4.Để chèn một phần tử mới xung quanh một phần tử khác, sử dung
.wrap()
.wrapAll()
.wrapInner()
5.Để thay thế một phần tử này với phần tử khác hoặc chữ sử dụng
.html()
.text()
.replaceAll()
.replaceWith()
6.Để loại bỏ một phần tử nằm trong một phần tử khác dùng
.empty()
7.Để loại bỏ một phần tử và con cái của nó trong một tài liệu mà không thực sự xoá nó dùng
.remove()
Tóm tắt
Trong chương này chúng ta đã tạo, sao chép, tái kết hợp và làm đẹp cho phần nội dung sử
dụng phương pháp sửa đổi DOM của jQuery. Chúng ta đã áp dụng những phương thức này
cho một trang web để biến những đoạn văn bình thường thành phần chú thích tự động, phần
trích dẫn, đường liên kết v.v..
Phần tutorial của cuốn sách cũng sắp hết, nhưng trước khi chúng ta học thêm những ví dụ
phức tạp hơn và mở rộng hơn. Chúng ta hãy dành chút thời gian để nghiên cứu phương thức
AJAX của jQuery. Trong chương kế tiếp, chúng ta hãy dạo một vòng lên server nhờ “phi
thuyền” AJAX.