HTML5 Drag and Drop功能DataTransfer物件format參數注意事項

Drag and Drop(DND)是HTML5裡一個趣味與實用性很高的一個功能,在實作時發現一個有趣的小問題。我們從程式碼來看問題比較好說明。

以下為http://msdn.microsoft.com/en-us/library/ie/ms536744(v=vs.85).aspx範例程式碼:

var sImageURL;
function InitiateDrag() 
/*  The setData parameters tell the source object
   to transfer data as a URL and provide the path.  */
{   
    event.dataTransfer.setData("URL", oImage.src);
}
function FinishDrag()
/*  The parameter passed to getData tells the target
    object what data format to expect.  */
{
    sImageURL = event.dataTransfer.getData("URL")
    oTarget.innerText = sImageURL;
}

以下為http://www.w3schools.com/html/html5_draganddrop.asp範例程式碼:

function allowDrop(ev)
{
ev.preventDefault();
}

function drag(ev)
{
ev.dataTransfer.setData("Text",ev.target.id);
}

function drop(ev)
{
ev.preventDefault();
var data=ev.dataTransfer.getData("Text");
ev.target.appendChild(document.getElementById(data));
}

問題點於在setData方法的format參數,完整是setData(format,data)。以上程式能正常執行,我們再看其他程式。

Knockout.js與MVVM基礎入門補充教材

Web API與MVVM

小弟有幸,被看得起,被找去寫書。所以未來幾個月,應該會有人生第一本電腦技術書藉在書局裡面供大家挑選,最大的願望當然是人手一本,你可以拿去當枕頭,然後睡到落枕…再起來寫程式,也可以拿去包豬肉,再拿來生火烤肉。

在寫書期間,發現,目前中文方面討論MVVM模式的資料實在太少,但要把全部MVVM模式相關寫進去書裡又離題太遠,書裡有一章是使用Knockout.js實作MVVM模式,索性就把官方那非常高互動的教材給翻譯+中文化,本想放入書中當補充教材。但實在想了很久,還是決定使用Blog加電子書方式來公開。Blog你可以線上看,電子書(PDF)可以在平板上看。

Knockoutjs基礎入門教材

如果你想更快速學習Knockout.js與MVVM模式,書會有圖解,會讓你看的一清二楚,不過想快速入門建議可以連上"http://learn.knockoutjs.com/"官方教材,配合本中文化不用多少時間,就可以清楚整體輪廓,打下不少基礎。

基礎打好,未來練功才不會內傷,這是最後決定放出來的原因。希望大家未來的功能練的順利。

下載:Knockoutjs基礎入門教材

Blog全文連結

  1. Knockout教學1 - Knockout.js與MVVM模式簡介
  2. Knockout.js教學2 - 清單與集合
  3. Knockout教學3 - Single Page Applications, SPA
  4. Knockout教學4 - 自訂繫結
  5. Knockout教學5 - 由伺服器載入與儲存資料

Knockout教學5 - 由伺服器載入與儲存資料

伺服器載入與儲存資料

現在,你已經瞭解MVVM模式如何為動態UI有組織地整理用戶端程式碼,以及knockout.js的可觀察物件(observables)、繫結(bindings)、相依追蹤(dependency tracking)是如何運作。

幾乎所有網站應用程式,都需要由伺服器取得資料和傳送修改的資料回去伺服器。因為Knockout.js 是一個純用戶端函式庫,它能非常有彈性運作在任何伺服器端技術(例如,ASP.NET、ASP.NET MVC、Rails、PHP…等),以及任何架構模式、資料庫…等。只要伺服器端程式碼可以傳送與接收JSON資料,就能呈現以下教學。

範例:工作清單

ViewModel:

function Task(data) {
    this.title = ko.observable(data.title);
    this.isDone = ko.observable(data.isDone);
}

function TaskListViewModel() {
    // 資料
    var self = this;
    self.tasks = ko.observableArray([]);
    self.newTaskText = ko.observable();
    self.incompleteTasks = ko.computed(function() {
        return ko.utils.arrayFilter(self.tasks(), function(task) { return !task.isDone() });
    });

    // 行為
    self.addTask = function() {
        self.tasks.push(new Task({ title: this.newTaskText() }));
        self.newTaskText("");
    };
    self.removeTask = function(task) { self.tasks.remove(task) };
}

ko.applyBindings(new TaskListViewModel());

從ViewModel定義我可以看出來這是一個工作清單,Task物件有抬頭與是否完成。TaskListViewModel物件清單新增、刪除與完成等處理。讀者現在應該已經具備看懂以上程式碼的能力。

View:

<h3>工作</h3>

<form data-bind="submit: addTask">
    新增工作: <input data-bind="value: newTaskText" placeholder="需要做些什麼?" />
    <button type="submit">新增</button>
</form>

<ul data-bind="foreach: tasks, visible: tasks().length > 0">
    <li>
        <input type="checkbox" data-bind="checked: isDone" />
        <input data-bind="value: title, disable: isDone" />
        <a href="#" data-bind="click: $parent.removeTask">刪除</a>
    </li> 
</ul>

你有 <b data-bind="text: incompleteTasks().length"> </b> 個未完成的工作。
<span data-bind="visible: incompleteTasks().length == 0"> - 現在是啤酒時間 !</span>

以下是此範列使用的CSS:

ul { list-style-type: none; margin: 1em 0; background-color: #cde; padding: 1em; border-radius: 0.5em;  }
ul li a { color: Gray; font-size: 90%; text-decoration: none }
ul li a:hover { text-decoration: underline }
input:not([type]), input[type=text] { width: 30em; }
input[disabled] { text-decoration: line-through; border-color: Silver; background-color: Silver; }
textarea { width: 30em; height: 6em; }
form { margin-top: 1em; margin-bottom: 1em; }

花點時間先玩一下這個範例,新增、刪除、完成一些清單內容。

Knockout教學4 - 自訂繫結

繫結(Bindings)

在Knockout.js來解釋MVVM,繫結是什麼內容要加入你的View與ViewModel。繫結是一媒介,它會雙向執行更新:

  • 繫結通知ViewModel改變且對應地更新View的DOM
  • 繫結截取DOM事件且對應地更新ViewModel屬性

Knockout.js有一套靈活和全面內建的繫結(像是text、click、foreach繫結等),但這不意味就此停止,你也可以使用少數程式碼來建立自訂繫結。在任何實際的應用程式中你會發現它有益來封裝常見的UI模式的繫結,這樣就能重覆的使用這些模式。

自訂繫結

例如,knockoutjs.com網站使用自訂繫結來封裝對話方塊,可拖動視窗,程式碼編輯器。

假設你有一個調查頁面的程式碼:

function Answer(text) { 
 this.answerText = text; 
 this.points = ko.observable(1); 
}

function SurveyViewModel(question, pointsBudget, answers) {
    var self = this;
    self.question = question;
    self.pointsBudget = pointsBudget;
    self.answers = $.map(answers, function(text) { return new Answer(text) });
    self.save = function() { alert('To do') };
                       
    self.pointsUsed = ko.computed(function() {
        var total = 0;
        for (var i = 0; i < this.answers.length; i++)
            total += this.answers[i].points();
        return total;        
    }, this);
}

ko.applyBindings(new SurveyViewModel("哪些因素會影響您的技術選擇?", 10, [
   "功能、相容性、定價-那些無聊的東西",
   "在駭客新聞上如何經常被提到",    
   "是否容易學習與使用",        
   "專案上的可信任度"
]));

View:

<h3 data-bind="text: question"></h3>
<p>請將 <b data-bind="text: pointsBudget"></b> 點平均分配至選項。</p>

<table>
    <thead><tr><th>選項</th><th>重要性</th></tr></thead>
    <tbody data-bind="foreach: answers">
        <tr>
            <td data-bind="text: answerText"></td>
            <td><select data-bind="options: [1,2,3,4,5], value: points"></select></td>
        </tr>    
    </tbody>
</table>

<h3 data-bind="visible: pointsUsed() > pointsBudget">你使用超過的點數,請刪減一些。</h3>
<p>你有 <b data-bind="text: pointsBudget - pointsUsed()"></b> 點可以使用。</p>
<button data-bind="enable: pointsUsed() <= pointsBudget, click: save">完成</button>

現在,我們要來改善此應用程式:

  • 使用動畫效果提示「你使用超過的點數,請刪減一些。」
  • 改善「完成」按鈕的樣式
  • 使用星星評分來換代下拉式選單給點數

Knockout教學3 - Single Page Applications, SPA

單頁應用程式

許多最現代、靈敏和基於Web的UI已超越傳統 Ajax,已成為單個頁面的應用程式(single page applications):訪客可以在單一的頁面巡覽像本地應用程序的速度。最著名的例子可能是GMail,但這些日子裡,它已經是一種越來越普遍的技術。

hash-based與pushState巡覽

這種應用程式使用hash-based或pushState巡覽以支援前進/後退手勢和書籤。如果讀者不熟悉這種技術的工作原理,請參閱以下簡短的解釋。

hash-based巡覽:訪問者的位置是儲存在URL hash的虛擬巡覽空間裡,hash符號的後面是URL的一部分(例如,/my/app/#category=shoes&page=4),每當URL hash有變動,瀏覽器不會發出HTTP請求來獲取新的一頁。相反的是,它只會把新的URL加入它的前進或後退歷史清單中,並在此網頁運作的腳本中公開並更新新URL hash。該腳本通知新URL hash和動態更新UI並顯示對應的項目內容(例如,”#category=shoes&page=4”代表鞋子目錄第四頁)。

這樣就可以在單個頁面應用程式中支援前進與後退按鈕巡覽(例如,按下”後退鍵”移至前一個URL hash),和有效地使用虛擬位置並可和書籤共同。

pushState是一個提供了另一種不同的方法來更改當前URL的HTML5 API,它將插入新的前進和後退和歷史項目,而不會觸發頁面載入。這不同於URL hash巡覽,因為不限於更新hash片段,可以更新整個URL。

範例:建置一個網頁郵件用戶端

你有一個簡單ViewModel,它們的內容只有保留資料夾清單。你的第一件工作是呈現它們在畫面上,而且讓它們是可選擇的。

你可以使用foreach繫結來呈顯資料夾清單,在View加入以上程式碼:

<!-- Folders -->
<ul data-bind="foreach: folders">
    <li data-bind="text: $data"></li>
</ul>

如果你現在執行應用程式,你應該有一個圓點清單(bullet-pointed)。這就是好的語意,但不是有吸引力。新增folders類別樣式到<ul>元素來改善:

<ul class="folders" data-bind="foreach: folders">

這使它看來起來更好。