而輔助這些互動功能的語法均需要使用 CSS 及 Javascript
在 Javascript 方面,由於得到 jQuery 的跨瀏覽器語法,在開發 Javascript 新功能時可以不再考慮瀏覽器的問題
可能不少網頁設計者都會遇過類似狀況
輸入文字欄位數量不夠,需要新加入一定數量的輸入文字爛位,但究竟多少才足夠卻不是設計師可以控制
最理想的狀況是由使用者在需要增加資料,增加輸入文字爛位
<table> <tbody> <tr> <td><input type="text"/></td> <td><input type="button" value="+"/></td> <td><input type="button" value="-"/></td> <td><input type="button" value="▼+"/></td> <td><input type="button" value="▲"/></td> <td><input type="button" value="▼"/></td> </tr> </tbody> </table>以上的 HTML 會以下面的方式顯示
當 - 被按下後,選定的列會被移除
當 ▼+ 被按下後,複製選定的列於下一列
當 ▲ 被按下後,選定的列向上移
當 ▼ 被按下後,選定的列向下移
網上有不少使用 jQuery 的教學達成這種操作
而這裡則是使用 DOM 的方式達成這種操作
先利用 Javascript 的 Prototype 補充原來的 Javascript 的不足之處
Prototype Javascript
// DOM 的 Node 有 nextSibling 及 previousSibling 屬性 // 來獲取 Node 的上一個 Node 及 下一個 Node ,當沒有 Node 時,會得到 null // 但 *Sibling 可能比較難記,可以自行增加 nextNode() 及 previousNode() 功能 // (Java 來說會以功能操作比較熟習) Node.prototype.nextNode = function(){ return this.nextSibling; } Node.prototype.previousNode = function(){ return this.previousSibling; } // 但上一個 Node 或下一個 Node 不一定是 Tag // 對於 DOM Tag 與 Tag 之間的空白字元都是 Node 只是 nodeType 的不同 // Tag 的 nodeType 為 1 Node.prototype.nextTag = function(){ var node = this.nextNode(); while (node != null && node.nodeType != 1){ node = node.nextNode(); } return node; } Node.prototype.previousTag = function(){ var node = this.previousNode(); while (node != null && node.nodeType != 1){ node = node.previousNode(); } return node; } // 獲取第一及最後的 Tag Node.prototype.firstTag = function(){ var node = this.firstChild; while (node != null && node.nodeType != 1){ node = node.nextNode(); } return node; } Node.prototype.lastTag = function(){ var node = this.lastChild; while (node != null && node.nodeType != 1){ node = node.previousNode(); } return node; } // DOM 的 Node 只有 insertBefore 功能卻沒有 insertAfter 功能 // 可以自行編寫 insertAfter 來補足功能 Node.prototype.insertAfter = function(new_node, existing_node){ if (new_node instanceof Node){ existing_node.parentNode.insertBefore(new_node, existing_node.nextNode()); } } // 讓兩個 DOM Node 的位置交換 Node.prototype.swap = function(node){ if (node instanceof Node){ var parent = this.parentNode; parent.insertBefore(node.cloneNode(true), this); parent.insertBefore(this.cloneNode(true), node); parent.removeChild(this); parent.removeChild(node); } } // 自編 insertRowBefore 及 insertRowAfter 功能 // 將新的 列 新增至 當前列 的 前 或 後 HTMLTableRowElement.prototype.insertRowBefore = function(row){ if (row instanceof HTMLTableRowElement){ this.parentNode.insertBefore(row, this); } } HTMLTableRowElement.prototype.insertRowAfter = function(row){ if (row instanceof HTMLTableRowElement){ this.parentNode.insertAfter(row, this); } }
Javascript
function add_row(input){ var td = input.parentNode; var tr = td.parentNode; var tx = tr.parentNode; var t_tr = tx.rows[0].cloneNode(true); t_tr.removeAttribute('style'); tr.insertRowAfter(t_tr); } function copy_row_input_value(tr1, tr2){ for (var i = 0; i < tr1.cells.length; i++){ var element = tr1.cells[i].firstChild; if (element instanceof HTMLInputElement){ if (element.type == 'radio' || element.type == 'checkbox'){ tr2.cells[i].firstChild.checked = element.checked; } else { tr2.cells[i].firstChild.value = element.value; } } else if (element instanceof HTMLSelectElement){ tr2.cells[i].firstChild.selectedIndex = element.selectedIndex; } else if (element instanceof HTMLTextAreaElement){ tr2.cells[i].firstChild.innerHTML = element.innerHTML; } } } function copy_row(input){ var td = input.parentNode; var tr = td.parentNode; var tx = tr.parentNode; var t_tr = tx.rows[0].cloneNode(true); t_tr.removeAttribute('style'); tr.insertRowAfter(t_tr); copy_row_input_value(tr, t_tr); } function delete_row(input){ var td = input.parentNode; var tr = td.parentNode; var tx = tr.parentNode; if (tx.rows.length <= 2){ add_row(input); } tx.removeChild(tr); } function move(input, direction){ var td = input.parentNode; var tr = td.parentNode; var tx = tr.parentNode; var t_tr = tr.cloneNode(true); copy_row_input_value(tr, t_tr); if (tr != tx.firstTag().nextTag() && direction == 'up'){ tx.insertBefore(t_tr, tr.previousTag()); tx.removeChild(tr); } else if (tr != tx.lastTag() && direction == 'down'){ tx.insertAfter(t_tr, tr.nextTag()); tx.removeChild(tr); } } function move_up(input){ move(input, 'up'); } function move_down(input){ move(input, 'down'); }HTML
<table> <tbody> <tr style="display: none;"> <td><input type="text"/></td> <td><input type="button" value="+" onclick="add_row(this);"/></td> <td><input type="button" value="-" onclick="delete_row(this);"/></td> <td><input type="button" value="▼+" onclick="copy_row(this);"/></td> <td><input type="button" value="▲" onclick="move_down(this);"/></td> <td><input type="button" value="▼" onclick="move_up(this);"/></td> </tr> <tr> <td><input type="text"/></td> <td><input type="button" value="+" onclick="add_row(this);"/></td> <td><input type="button" value="-" onclick="delete_row(this);"/></td> <td><input type="button" value="▼+" onclick="copy_row(this);"/></td> <td><input type="button" value="▲" onclick="move_down(this);"/></td> <td><input type="button" value="▼" onclick="move_up(this);"/></td> </tr> </tbody> </table>由於第一列使用 display: none 來隱藏列,因此複製的列需要移除 style 屬性
另外由於 input, select, textarea 等 表單元素 不會複製由用戶輸入的 value, checked, selected, innerHTML 等資料
複製列時需要連同 value, checked, selected, innerHTML 等資料一拼複製才能達至複製效果
以下是示範
這種方法的可取之處是不需要使用 id, class 之類的 CSS Selector 達到效果
沒有留言 :
張貼留言