DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> JavaScript入門知識 >> JavaScript基礎知識 >> JavaScript CSS修改學習第六章 拖拽
JavaScript CSS修改學習第六章 拖拽
編輯:JavaScript基礎知識     

當示例的box上的#鏈接處於活動狀態的時候(不論是用tab然後點擊enter或者使用鼠標點擊)這個元素就能夠通過方向鍵拖拽。然後點擊enter或者Esc釋放。(可以隨意改變這些鍵。我不確定釋放鍵應該設置成為什麼所以enter和Esc都可以)

 

使用

1、復制文章後面的dragDrop對象。

2、復制我的addEventSimple和removeEventSimple函數,這裡需要。

3、設定keyHTML和keySpeed屬性(下面有解釋)。

4、確定你所要拖拽的元素都有位置屬性:absolute或者fixed。

5、把所有可拖拽的元素發送到對象的initElement函數。可以發送一個對象或者對象ID的字符串。例如:

dragDrop.initElement('test');
dragDrop.initElement(document.getElementById('test2'));

6、當元素被拖拽過後,代碼會自動添加dragged類。你可以添加一些CSS效果。

7、如果你想當用戶放開元素之後做一些事情,你可以給releaseElement添加自己的函數。

屬性

你需要設置兩個屬性。

keyHTML包含一個需要拖拽的元素的鍵盤能訪問到的鏈接的內容。為了保持HTML簡潔,這裡只添加一個有簡單樣式的類。你可以隨意構建你的HTML,但是要記住一點就是必須有一個鏈接讓鍵盤能夠訪問到,鍵盤用戶需要一個焦點來觸發拖拽事件。

keySpeed用來設置鍵盤拖拽的速度,每次按鍵移動多少像素。我喜歡設置為10,你也可以嘗試一下其他的值。

這裡還有7個屬性,但是都是在代碼內部的。初始化的時候都設置為undefined,然後相應的函數會設置他們。

 

拖拽對象


復制下面這個對象到你的頁面,不要忘了addEventSimple和removeEventSimple。
復制代碼 代碼如下:
dragDrop = {
    keyHTML: '<a href="#" class="keyLink">#</a>',
    keySpeed: 10, // pixels per keypress event
    initialMouseX: undefined,
    initialMouseY: undefined,
    startX: undefined,
    startY: undefined,
    dXKeys: undefined,
    dYKeys: undefined,
    draggedObject: undefined,
    initElement: function (element) {
        if (typeof element == 'string')
            element = document.getElementById(element);
        element.onmousedown = dragDrop.startDragMouse;
        element.innerHTML += dragDrop.keyHTML;
        var links = element.getElementsByTagName('a');
        var lastLink = links[links.length-1];
        lastLink.relatedElement = element;
        lastLink.onclick = dragDrop.startDragKeys;
    },
    startDragMouse: function (e) {
        dragDrop.startDrag(this);
        var evt = e || window.event;
        dragDrop.initialMouseX = evt.clientX;
        dragDrop.initialMouseY = evt.clientY;
        addEventSimple(document,'mousemove',dragDrop.dragMouse);
        addEventSimple(document,'mouseup',dragDrop.releaseElement);
        return false;
    },
    startDragKeys: function () {
        dragDrop.startDrag(this.relatedElement);
        dragDrop.dXKeys = dragDrop.dYKeys = 0;
        addEventSimple(document,'keydown',dragDrop.dragKeys);
        addEventSimple(document,'keypress',dragDrop.switchKeyEvents);
        this.blur();
        return false;
    },
    startDrag: function (obj) {
        if (dragDrop.draggedObject)
            dragDrop.releaseElement();
        dragDrop.startX = obj.offsetLeft;
        dragDrop.startY = obj.offsetTop;
        dragDrop.draggedObject = obj;
        obj.className += ' dragged';
    },
    dragMouse: function (e) {
        var evt = e || window.event;
        var dX = evt.clientX - dragDrop.initialMouseX;
        var dY = evt.clientY - dragDrop.initialMouseY;
        dragDrop.setPosition(dX,dY);
        return false;
    },
    dragKeys: function(e) {
        var evt = e || window.event;
        var key = evt.keyCode;
        switch (key) {
            case 37:    // left
            case 63234:
                dragDrop.dXKeys -= dragDrop.keySpeed;
                break;
            case 38:    // up
            case 63232:
                dragDrop.dYKeys -= dragDrop.keySpeed;
                break;
            case 39:    // right
            case 63235:
                dragDrop.dXKeys += dragDrop.keySpeed;
                break;
            case 40:    // down
            case 63233:
                dragDrop.dYKeys += dragDrop.keySpeed;
                break;
            case 13:     // enter
            case 27:     // escape
                dragDrop.releaseElement();
                return false;
            default:
                return true;
        }
        dragDrop.setPosition(dragDrop.dXKeys,dragDrop.dYKeys);
        if (evt.preventDefault)
            evt.preventDefault();
        return false;
    },
    setPosition: function (dx,dy) {
        dragDrop.draggedObject.style.left = dragDrop.startX + dx + 'px';
        dragDrop.draggedObject.style.top = dragDrop.startY + dy + 'px';
    },
    switchKeyEvents: function () {
        // for Opera and Safari 1.3
        removeEventSimple(document,'keydown',dragDrop.dragKeys);
        removeEventSimple(document,'keypress',dragDrop.switchKeyEvents);
        addEventSimple(document,'keypress',dragDrop.dragKeys);
    },
    releaseElement: function() {
        removeEventSimple(document,'mousemove',dragDrop.dragMouse);
        removeEventSimple(document,'mouseup',dragDrop.releaseElement);
        removeEventSimple(document,'keypress',dragDrop.dragKeys);
        removeEventSimple(document,'keypress',dragDrop.switchKeyEvents);
        removeEventSimple(document,'keydown',dragDrop.dragKeys);
        dragDrop.draggedObject.className = dragDrop.draggedObject.className.replace(/dragged/,'');
        dragDrop.draggedObject = null;
    }
}

拖拽是什麼
拖拽是在屏幕上移動元素的一種方法。為了讓元素能夠移動,元素必須有position屬性:absolute或者fixed,這樣才能通過修改它的坐標(style.top和style.left)讓它移動。
(理論上position:relative也可以,但是幾乎沒用。另外,那樣需要額外的數據來計算,這裡我沒有寫)
設置坐標很簡單;找到需要設置的元素的坐標是這個代碼比較難的部分。大多數代碼都是用來處理這個問題的。
另外,保持易用性也比較重要。傳統上通過鼠標來拖拽一個元素是最好的辦法,但是也要考慮到沒有鼠標的用戶,所以也要保證鍵盤的可用性。

基礎知識
讓我們先來看看一些基礎知識
初始化一個元素
每個拖拽代碼都從初始化元素開始。這個工作通過下面的函完成:
復制代碼 代碼如下:
initElement: function (element) {
    if (typeof element == 'string')
        element = document.getElementById(element);
    element.onmousedown = dragDrop.startDragMouse;
    element.innerHTML += dragDrop.keyHTML;
    var links = element.getElementsByTagName('a');
    var lastLink = links[links.length-1];
    lastLink.relatedElement = element;
    lastLink.onclick = dragDrop.startDragKeys;
},

如果函數接收到一個字符串,那麼就會當做元素ID來處理。然後給這個元素設置一個onmousedown事件,用來開始鼠標部分的代碼。注意這裡我使用的是傳統事件注冊方式;因為我希望this關鍵字能夠在startDragDrop裡起作用。

然後把用戶定義的keyHTML添加到元素上,我相信這個鏈接是用來觸發鍵盤事件的。然後為這個鏈接設置鍵盤的觸發程序。然後存儲主元素在relatedElement裡面,我們後面需要。

現在代碼就等用戶動作了

基本位置信息
我打算使用下面的方法來:首先我會讀取拖拽元素的初始位置,保存在startX和startY裡面。然後計算鼠標移動的位置或者鍵盤控制下移動的位置來決定元素從初始位置移動的范圍。
image 
startX和startY通過startDrag函數來設置,這個函數在鼠標和鍵盤事件裡都會用到。
復制代碼 代碼如下:
startDrag: function (obj) {
    if (dragDrop.draggedObject)
        dragDrop.releaseElement();
    dragDrop.startX = obj.offsetLeft;
    dragDrop.startY = obj.offsetTop;
    dragDrop.draggedObject = obj;
    obj.className += ' dragged';
},

首先,如果元素處於拖拽狀態,那麼我們就釋放他(我們後面再講)。
然後函數會找到元素在起始位置的坐標(offsetLeft和offsetTop),然後保存在startX和startY以備後用。
然後在draggedObject裡面保存一個對象的引用。然後給他添加dragged類,這樣就可以通過CSS來設置拖拽時候的樣式了
當用戶使用鼠標或者鍵盤拖拽元素的時候,代碼的最復雜的部分就要跟蹤位置的變化。然後給出dX和dY(X和Y的變化)。然後加上startX和startY就是元素現在的位置。
下面的函數用來設定位置:
復制代碼 代碼如下:
setPosition: function (dx,dy) {
    dragDrop.draggedObject.style.left = dragDrop.startX + dx + 'px';
    dragDrop.draggedObject.style.top = dragDrop.startY + dy + 'px';
},

函數通過鼠標和鍵盤的移動計算所得的dX和dY與初始位置,來設定元素的新位置。
這部分很簡單,復雜的地方就在於dx和dy的獲得,鼠標部分和鍵盤部分的處理非常的不同,我們分別來看。

鼠標部分的代碼
鼠標部分的計算方面比鍵盤的要復雜一些,但是在浏覽器兼容性方面問題不大。所以我們從鼠標部分開始。
事件
首先我們來討論事件。很明顯的,在拖拽過程中需要mousedown,mousemove,mouseup用來完成選擇對象,拖拽,拖拽完成這幾個動作。
這一系列事件從需要拖拽元素的mousedown事件開始。所以所有的拖拽元素都需要這個事件來標明拖拽開始了。我們看到:
復制代碼 代碼如下:element.onmousedown = dragDrop.startDragMouse;
然而mousemove和mouseup事件不應該設置在元素上而應該設置在整個文檔上。因為用戶可能很快很瘋狂的移動鼠標,然後丟失拖拽元素。如果設置在元素上,因為鼠標不在元素上了所以可能會無法控制,這對於易用性來說不是好事。
如果我們再文檔上設置mousemove和mouseup,就沒有這個問題。不管鼠標在哪,元素都會響應mousemove和mouseup。這個的易用性就很強。
另外你只能在拖拽開始以後再設置mousemove和mouseup,然後當用戶釋放元素之後刪除它們。這樣代碼很干淨而且節省系統資源,因為mousemove對系統的消耗很大。
Mousedown
當拖拽元素發生mousedown事件的時候,startDragMouse函數就開始執行:

首先會執行我們之前討論過的startDrag。然後查找鼠標的坐標然後保存在initialMouseX和initialMouseY中。後面我們會把鼠標位置跟這個比較。

最後會返回false,這個用來阻止默認鼠標事件:選擇文本。我們不想再拖拽的時候有文本被選中,這很煩人。

image

然後給文檔設置mouseup和mousemove事件處理程序。因為有可能文檔有他自己的mouseup和mousemove事件處理程序,所以我使用我的addEventSimple函數防止原來的事件處理程序失效。
Mousemove
現在,當用戶移動鼠標的時候dragMouse函數就執行了。

復制代碼 代碼如下:
dragMouse: function (e) {
    var evt = e || window.event;
    var dX = evt.clientX - dragDrop.initialMouseX;
    var dY = evt.clientY - dragDrop.initialMouseY;
    dragDrop.setPosition(dX,dY);
    return false;
},

這個函數會讀取鼠標現在的坐標,然後減去之前的坐標,把得到的dX和dY傳遞給sePosition。

然後通過返回false來阻止鼠標選擇文本的默認屬性。

image

Mouseup

當用戶松開鼠標的時候,會調用releaseElement。我們後面討論。

 

鍵盤部分代碼

現在我們開始更復雜的鍵盤部分代碼。不像鼠標拖拽那樣,鍵盤拖拽並沒有一個標准。雖說基本的交互不是太復雜,但是最好還是簡要說明一下。

基本交互

用來拖拽的鍵最好是方向鍵,這很簡單。

激活和釋放元素是比較有技巧的,在這裡我的代碼還需要加強。

我覺得如果用鍵盤來激活的話就應該使用一個我添加的額外的鏈接。這裡沒有太多選擇:因為鏈接能夠在所有的浏覽器裡面獲得焦點(好吧,表單也可以,你也可是選擇復選框),而且把一個鏈接放置在可拖拽的元素裡面也是合乎邏輯的(你可以放在任何地方,但是如何讓用戶知道那個是用來激活拖拽的呢?)。

我假設當用戶點擊enter或者Esc的時候釋放元素,至少我沒找到其他合適的鍵。你想選擇其他的話可以在這裡查找鍵盤代碼。

case 13: // enter
case 27: // escape
dragDrop.releaseElement();
return false;

 

事件

點擊可以激活元素。當鼠標點擊鏈接或者當元素獲得焦點的時候點擊enter鍵就能激活。所以鍵盤代碼的激活可以使點擊enter鍵或者點擊鏈接。

(嚴格來說,當你用鼠標點擊鏈接的時候,元素先被鼠標事件激活然後釋放了然後再被鍵盤模式激活。)

事件的其余部分也非常的模糊。當你想檢測方向鍵的時候鍵盤事件尤為麻煩。

首先我們需要一個允許重復點擊的事件,因為用戶可能按著方向鍵不放,那麼事件就需要一遍遍的觸發,這樣拖拽才能繼續。所以我們使用keypress事件。

不幸的是,IE在keypress的情況下不支持方向鍵。在IE裡面keydown會重復發生,看起來我們需要使用keydown事件了。

你可能才到事情沒那麼簡單。在Opera和Safari裡面keydown事件只能觸發一次,所以當用戶按下鍵之後,元素移動一次之後就不動了。在這些浏覽器中我們需要keypress。

所以理想情況下,我們使用keypress,如果不支持就是用keydown。但是怎麼切換事件呢?你又怎麼知道keypress在這個時候不能用呢?

我的解決辦法就是給keypress事件設置一個事件處理程序。如果這個程序執行了說明支持keypress,我們就可以安全的切換了。

startDragKeys函數用來設置keydown和keypress事件:
復制代碼 代碼如下:
addEventSimple(document,'keydown',dragDrop.dragKeys);
addEventSimple(document,'keypress',dragDrop.switchKeyEvents);

首先keydown觸發完成拖拽的dragKeys函數。這是第一個觸發的事件,而且元素總會移動。然後我們做其他的話,那麼元素在Opera和Safari1.3裡面移動一次以後就會停止。
這就是為什麼我們還需要keypress。第一個keypress事件會觸發switchKeyEvents函數,這個函數會調整事件處理程序:
復制代碼 代碼如下:
switchKeyEvents: function () {
    removeEventSimple(document,'keydown',dragDrop.dragKeys);
    removeEventSimple(document,'keypress',dragDrop.switchKeyEvents);
    addEventSimple(document,'keypress',dragDrop.dragKeys);
},

他會先刪除掉原來的事件處理程序,然後將keypress設置為觸發dragKeys。因為這個函數只會在支持他的浏覽器裡面執行,所以我們只在這些浏覽器裡面將keydown改為keypress。
初始鍵盤代碼
當用戶點擊了連接激活了元素,那麼就會調用startDragKeys。
復制代碼 代碼如下:
startDragKeys: function () {
    dragDrop.startDrag(this.relatedElement);
    dragDrop.dXKeys = dragDrop.dYKeys = 0;
    addEventSimple(document,'keydown',dragDrop.dragKeys);
    addEventSimple(document,'keypress',dragDrop.switchKeyEvents);
    this.blur();
    return false;
},

首先會調用我們之前討論過的startDrag函數。他會給這個函數傳遞relatedElement,也就是要拖拽的元素。
然後將dXKeys和dYKeys設置為0。這些變量用來跟蹤元素的位移。
然後設置事件處理程序,上面已經討論過了。
然後移除剛才點擊的鏈接的焦點。我這樣做是因為Enter鍵會釋放元素,但是如果不移除焦點,當用戶點擊了Enter鍵之後,元素被釋放,但是鏈接卻再次被Enter點擊,又成了可拖動的模式。如果我們移除焦點,那麼問題就不存在了。
最後返回false來阻止默認動作。
通過鍵盤拖拽
dragKeys負責鍵盤拖拽:
復制代碼 代碼如下:
dragKeys: function(e) {
    var evt = e || window.event;
    var key = evt.keyCode;

我們首先讀取鍵盤的鍵值。
然後我們使用switch語句來決定我們怎麼做。這部分的目的是更新dXKeys和dYKeys的值,就可以通過設置元素的位置來移動元素了。
復制代碼 代碼如下:
switch (key) {
    case 37:    // left
    case 63234:
        dragDrop.dXKeys -= dragDrop.keySpeed;
        break;
    case 38:    // up
    case 63232:
        dragDrop.dYKeys -= dragDrop.keySpeed;
        break;
    case 39:    // right
    case 63235:
        dragDrop.dXKeys += dragDrop.keySpeed;
        break;
    case 40:    // down
    case 63233:
        dragDrop.dYKeys += dragDrop.keySpeed;
        break;

作者通過設置keySpeed來確定每次移動的像素大小。當用戶點擊左方向鍵,就減去keySpeed。
image 
這個代碼包含63232-63235的情況。因為Safari1.3沒有使用標准的37-40的方向鍵的鍵值(Safari 3已經支持了)。

復制代碼 代碼如下:case 13: // enter
case 27: // escape
dragDrop.releaseElement();
return false;

如果用戶點擊Enter或者Esc鍵,就調用releaseElement()函數。如果你想改變釋放元素的按鍵,可以再這裡添加。

復制代碼 代碼如下:default:
return true;
}
如果用戶按下了其他鍵,就執行默認動作並且結束函數。

復制代碼 代碼如下: dragDrop.setPosition(dragDrop.dXKeys,dragDrop.dYKeys);
現在dXKeys和dYKeys已經更新我們發送到setPosition()函數中來改變元素的位置。

復制代碼 代碼如下:if (evt.preventDefault)
evt.preventDefault();
return false;
},
最後我們需要阻止默認事件,如果用戶點擊下方向鍵,那麼在執行完上面的代碼後頁面會向下滾動。W3C兼容浏覽器中通過preventDefault實現,在IE中通過返回false實現。


釋放元素
當用戶釋放了元素,函數releaseElement就會被調用。他會移除所有代碼設置的事件處理程序,移除dragged類,清理draggedObject然後等待用戶動作。
復制代碼 代碼如下:
releaseElement: function() {
    removeEventSimple(document,'mousemove',dragDrop.dragMouse);
    removeEventSimple(document,'mouseup',dragDrop.releaseElement);
    removeEventSimple(document,'keypress',dragDrop.dragKeys);
    removeEventSimple(document,'keypress',dragDrop.switchKeyEvents);
    removeEventSimple(document,'keydown',dragDrop.dragKeys);
    dragDrop.draggedObject.className = dragDrop.draggedObject.className.replace(/dragged/,'');
    dragDrop.draggedObject = null;
}

你或許在用戶釋放元素之後還想做些什麼,可以把你的函數添加在這裡。
翻譯地址:http://www.quirksmode.org/js/dragdrop.html
轉載請保留以下信息
作者:北玉(tw:@rehawk)

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved