最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

相當流暢的JavaScript拖拽排序?qū)崿F(xiàn)方法代碼

 更新時間:2026年04月09日 08:33:12   作者:左右飛  
拖拽排序是一種在網(wǎng)頁設計和應用程序中常見的交互方式,允許用戶通過鼠標或觸摸操作來重新排列頁面或界面上的元素,這篇文章主要介紹了相當流暢的JavaScript拖拽排序?qū)崿F(xiàn)的相關資料,需要的朋友可以參考下

01

拖拽排序是一種在網(wǎng)頁設計和應用程序中常見的交互方式,允許用戶通過鼠標或觸摸操作來重新排列頁面或界面上的元素。這種交互方式對于提升用戶體驗和操作效率具有重要意義。

在拖拽排序中,用戶可以用鼠標或手指按住某個元素,然后將其拖動到新

的位置,從而實現(xiàn)對元素的重新排列。這種操作直觀且靈活,使得用戶可以根據(jù)自己的需求隨時調(diào)整頁面或界面的布局,提升了個性化體驗。同時,拖拽排序也增加了用戶的參與度和粘性,用戶可以通過自由選擇和排序感興趣的內(nèi)容,提升留存率和活躍度。

從技術實現(xiàn)的角度來看,拖拽排序主要依賴于前端技術的支持。例如,基于JavaScript的實現(xiàn)方法主要是通過監(jiān)聽鼠標或觸摸事件來實現(xiàn)。在拖拽開始時,需要記錄拖拽元素的位置,然后在拖拽過程中更新元素的位置,最后在拖拽結束時判斷元素與其他元素的位置關系并進行排序。

在拖拽排序的應用場景中,列表排序和圖片排序是兩個典型的例子。在列表排序中,用戶可以通過拖動列表項來改變它們的順序,這在任務管理應用、待辦事項列表等場景中非常常見。在圖片排序中,用戶可以通過拖動圖片來改變它們的順序,這在圖片庫或相冊應用中較為常見。

02

在HTML中,我們給需要拖動的元素加上draggable="true"就可以實現(xiàn)拖拽效果了。在CSS中,我們設置了列表和拖拽項的樣式。

<div class="list">
    <div draggable="true" class="list-item">1</div>
    <div draggable="true" class="list-item">2</div>
    <div draggable="true" class="list-item">3</div>
    <div draggable="true" class="list-item">4</div>
    <div draggable="true" class="list-item">5</div>
    <div draggable="true" class="list-item">6</div>
</div>
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}
body {
    display: flex;
    justify-content: center;
}
.list {
    width: 600px;
    margin-top: 100px;
}
.list-item {
    margin: 6px 0;
    padding: 0 20px;
    line-height: 40px;
    height: 40px;
    background: #409eff;
    color: #fff;
    text-align: center;
    cursor: move;
    user-select: none;
    border-radius: 5px;
}

效果如下:

元素是可以拖拽了,但是拖拽時元素本身的樣式要改變,我們只需要給元素加上一個類樣式就可以了。那么,什么時候添加這個類呢?當然是開始拖動的時候,我們使用了HTML5的拖放API ondragstart,它是在用戶開始拖動元素時觸發(fā)。

2.1 拖拽開始

我們找到拖拽項的父元素,用事件委托的方式找到父元素,也就是.list并給它注冊一個ondragstart事件,當拖拽開始時,可以使用event.target來獲取被拖拽的元素,給它的類型樣式添加一個moving。

.list-item.moving {
    background: transparent;
    color: transparent;
    border: 1px dashed #ccc;
}
const list = document.querySelector('.list');
list.ondragstart = (e) => {
    setTimeout(() => {
        e.target.classList.add('moving')
    }, 0)
}

為什么要加setTimeout呢?因為跟隨鼠標的樣式取決于拖拽開始時元素本身的樣式,拖轉(zhuǎn)開始時把元素的樣式改變了,那就意味著跟隨鼠標的樣式也改變了,我們可以加一個setTimeout變成異步,在拖拽開始時還是保持原來的樣式,然后過一點點時間在變成添加moving的樣式。

2.2 拖拽過程

(1)當被拖拽的元素移動到另一個列表項上方時,會不斷觸發(fā)dragover事件。

(2)默認情況下,瀏覽器不允許放置(drop)操作,因此需要阻止這個事件的默認行為。這可以通過調(diào)用event.preventDefault()方法來實現(xiàn)。

ondragover: 當某被拖動的對象在另一對象容器范圍內(nèi)拖動時觸發(fā)此事件。

list.ondragover = (e) => {
    e.preventDefault();
}

(3)當用戶釋放鼠標按鈕,且被拖拽的元素位于一個有效的放置目標上方時,drop事件被觸發(fā)。

(4)在drop事件處理程序中,首先需要獲取拖拽源元素,接著獲取放置目標元素,這通常是觸發(fā)drop事件的元素。

(5)然后,需要更新DOM來反映新的排序。這通常涉及改變元素的位置,可以通過直接操作DOM(如insertBefore或appendChild)來實現(xiàn)。

ondragenter:當被鼠標拖動的對象進入其容器范圍內(nèi)時觸發(fā)此事件。

const list = document.querySelector('.list');
// 記錄被拖拽的元素
let sourceNode;
list.ondragstart = (e) => {
    setTimeout(() => {
        e.target.classList.add('moving')
    }, 0)
    // 記錄被拖拽的元素
    sourceNode = e.target;
}
list.ondragover = (e) => {
    e.preventDefault();
}
list.ondragenter = e => {
    e.preventDefault();
    // 判斷拖拽元素進入的元素等于父元素list或等于拖拽元素本身,
    // 不做受任何處理,直接結束
    if(e.target === list || e.target === sourceNode) {
        return;
    }
    // 判斷元素拖拽進入的位置是在目標的上面還是下面,
    // 比如拖動3進入到4時,4要移動到上面,
    // 當拖動3進入到2時,2要移動到下面,
    // 通過元素所處的下表既可判斷。
    // 首先,拿到元素list所有的子元素
    const children = [...list.children];
    // 接著,拿到要拖拽元素在整個子元素里面的下標
    const sourceIndex = children.indexOf(sourceNode);
    // 然后,拿到要進入目標元素在整個子元素里面的下標
    const targetIndex = children.indexOf(e.target);
    if(sourceIndex < targetIndex) {
        // 進入目標元素大于拖拽元素的下標,
        // 此時要插入目標元素的下方位置,
        // 也就是目標元素下一個元素的前面
        list.insertBefore(sourceNode, e.target.nextElementSibling);
    } else {
        // 進入目標元素小于拖拽元素的下標,
        // 此時要插入目標元素的上方位置,
        // 也就是目標元素前面的位置
        list.insertBefore(sourceNode, e.target);
    }
}

2.3 拖拽結束

ondragend:用戶完成元素拖動后觸發(fā)。

list.ondragend = () => {
  sourceNode.classList.remove('moving');
}

拖拽結束時,只需要把moving的樣式移除即可。

03

為了使元素位置改變時不那么生硬,可能需要提供一些額外的反饋,可以通過動畫來平滑地展示元素位置的改變。那么我們來了解一種動畫——Flip動畫。什么是Flip動畫呢?

Flip技術可以讓我們的動畫更加流暢,同時也能降低復雜動畫的開發(fā)難度。其實,F(xiàn)lip是幾個英文單詞的縮寫。

F:Fist —— 一個元素的起始位置。

L:Last —— 另一個元素的終止位置,注意另一個這個詞,后面會有具體代碼的體現(xiàn)。

I:Invert —— 計算"F"與"L"的差異,包括位置,大小等,并將差異用transform屬性,添加到終止元素上,讓它回到起始位置,也是此項技術的核心。

P:Play —— 添加transtion 過渡效果,清除Invert階段添加進來transform,播放動畫。

直接上帶代碼:

// Flip.js
const Flip = (function () {
 class FlipDom {
  constructor(dom, duration = 0.5) {
   this.dom = dom;
   this.transition =
    typeof duration === 'number' ? `${duration}s` : duration;
   this.firstPosition = {
    x: null,
    y: null,
   };
   this.isPlaying = false;
   this.transitionEndHandler = () => {
    this.isPlaying = false;
    this.recordFirst();
   }
  }
  getDomPosition() {
   const rect = this.dom.getBoundingClientRect();
   return {
    x: rect.left,
    y: rect.top,
   }
  }
  recordFirst(firstPosition) {
   if (!firstPosition) {
    firstPosition = this.getDomPosition()
   }
   this.firstPosition.x = firstPosition.x;
   this.firstPosition.y = firstPosition.y;
  }
  * play() {
   if (!this.isPlaying) {
    this.dom.style.transition = 'none';
    const lastPosition = this.getDomPosition();
    const dis = {
     x: lastPosition.x - this.firstPosition.x,
     y: lastPosition.y - this.firstPosition.y,
    }
    if (!dis.x && !dis.y) {
     return;
    }
    this.dom.style.transform = `translate(${-dis.x}px, ${-dis.y}px)`;
    yield 'moveToFirst';
    this.isPlaying = true;
   }
   this.dom.style.transition = this.transition;
   this.dom.style.transform = 'none';
   this.dom.removeEventListener('transitionend', this.transitionEndHandler);
   this.dom.addEventListener('transitionend', this.transitionEndHandler);
  }
 }
 class Flip {
  constructor(doms, duration = 0.5) {
   this.flipDoms = [...doms].map((it) => new FlipDom(it, duration));
   this.flipDoms = new Set(this.flipDoms);
   this.duration = duration;
   this.flipDoms.forEach((it) => it.recordFirst());
  }
  addDom(dom, firstPosition) {
   const flipDom = new FlipDom(dom, this.duration);
   this.flipDoms.add(flipDom)
   flipDom.recordFirst(firstPosition)
  }
  play() {
   let gs = [...this.flipDoms].map((it) => {
     const generator = it.play();
     return {
      generator,
      iteratorResult: generator.next()
     }
    })
    .filter((g) => !g.iteratorResult.done);
   while (gs.length > 0) {
    document.body.clientWidth;
    gs = gs.map((g) => {
      g.iteratorResult = g.generator.next();
      return g;
     })
     .filter((g) => !g.iteratorResult.done);
   }
  }
 }
 return Flip;
})();

完整代碼如下:

<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title></title>
  <style>
   * {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
   }
   body {
    display: flex;
    justify-content: center;
   }
   .list {
    width: 600px;
    margin-top: 100px;
   }
   .list-item {
    margin: 6px 0;
    padding: 0 20px;
    line-height: 40px;
    height: 40px;
    background: #409eff;
    color: #fff;
    text-align: center;
    cursor: move;
    user-select: none;
    border-radius: 5px;
   }
   .list-item.moving {
    background: transparent;
    color: transparent;
    border: 1px dashed #ccc;
   }
  </style>
 </head>
 <body>
  <div class="list">
   <div draggable="true" class="list-item">1</div>
   <div draggable="true" class="list-item">2</div>
   <div draggable="true" class="list-item">3</div>
   <div draggable="true" class="list-item">4</div>
   <div draggable="true" class="list-item">5</div>
   <div draggable="true" class="list-item">6</div>
  </div>
 </body>
 <script src="./flip.js"></script>
 <script>
  const list = document.querySelector('.list');
  // 記錄被拖拽的元素
  let sourceNode;
  let flip;
  list.ondragstart = (e) => {
   setTimeout(() => {
    e.target.classList.add('moving')
   }, 0)
   sourceNode = e.target;
   flip = new Flip(list.children, 0.5);
  }
  list.ondragover = (e) => {
   e.preventDefault();
  }
  list.ondragenter = e => {
   e.preventDefault();
   // 判斷拖拽元素進入的元素等于父元素list或等于拖拽元素本身,
   // 不做受任何處理,直接結束
   if(e.target === list || e.target === sourceNode) {
    return;
   }
   // 判斷元素拖拽進入的位置是在目標的上面還是下面,
   // 比如拖動3進入到4時,4要移動到上面,
   // 當拖動3進入到2時,2要移動到下面,
   // 通過元素所處的下表既可判斷。
   // 首先,拿到元素list所有的子元素
   const children = [...list.children];
   // 接著,拿到要拖拽元素在整個子元素里面的下標
   const sourceIndex = children.indexOf(sourceNode);
   // 然后,拿到要進入目標元素在整個子元素里面的下標
   const targetIndex = children.indexOf(e.target);
   if(sourceIndex < targetIndex) {
    // 進入目標元素大于拖拽元素的下標,
    // 此時要插入目標元素的下方位置,
    // 也就是目標元素下一個元素的前面
    list.insertBefore(sourceNode, e.target.nextElementSibling);
   } else {
    // 進入目標元素小于拖拽元素的下標,
    // 此時要插入目標元素的上方位置,
    // 也就是目標元素前面的位置
    list.insertBefore(sourceNode, e.target);
   }
   // 調(diào)用flip動畫play方法
   flip.play();
  }
  list.ondragend = () => {
   sourceNode.classList.remove('moving');
  }
 </script>
</html>

到此這篇關于JavaScript拖拽排序?qū)崿F(xiàn)方法代碼的文章就介紹到這了,更多相關js拖拽排序?qū)崿F(xiàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 查看Next.js默認配置信息的幾種方法

    查看Next.js默認配置信息的幾種方法

    文章介紹了幾種查看Next.js默認配置信息的方法,包括使用Next.js CLI、getConfigAPI、實驗性CLI命令以及在開發(fā)模式下訪問特定路徑,感興趣的小伙伴跟著小編一起來看看吧
    2025-02-02
  • JS實現(xiàn)多物體運動的方法詳解

    JS實現(xiàn)多物體運動的方法詳解

    這篇文章主要介紹了JS實現(xiàn)多物體運動的方法,結合實例形式較為詳細的分析了javascript實現(xiàn)多物體運動的原理與相關操作技巧,需要的朋友可以參考下
    2018-01-01
  • JavaScript實現(xiàn)圓形進度條效果

    JavaScript實現(xiàn)圓形進度條效果

    這篇文章主要為大家詳細介紹了JavaScript實現(xiàn)圓形進度條效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • JavaScript如何實現(xiàn)對數(shù)字保留兩位小數(shù)一位自動補零

    JavaScript如何實現(xiàn)對數(shù)字保留兩位小數(shù)一位自動補零

    本文給大家介紹基于JavaScript如何實現(xiàn)對數(shù)字保留兩位小數(shù)一位自動補零的實例代碼,代碼簡單易懂,感興趣的朋友一起學習吧
    2015-12-12
  • js實現(xiàn)淘寶瀏覽商品放大鏡功能

    js實現(xiàn)淘寶瀏覽商品放大鏡功能

    這篇文章主要為大家詳細介紹了js實現(xiàn)淘寶瀏覽商品放大鏡功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-10-10
  • 一文詳解如何跳出map或者foreach循環(huán)

    一文詳解如何跳出map或者foreach循環(huán)

    javascript中的遍歷方法有很多,今天主要總結一下這些遍歷方法如何跳出循環(huán),這篇文章主要給大家介紹了關于如何跳出map或者foreach循環(huán)的相關資料,需要的朋友可以參考下
    2023-11-11
  • JS數(shù)組進階示例【數(shù)組的幾種函數(shù)用法】

    JS數(shù)組進階示例【數(shù)組的幾種函數(shù)用法】

    這篇文章主要介紹了JS數(shù)組進階,結合實例形式總結分析了數(shù)組的幾種常見函數(shù)基本用法,涉及JavaScript數(shù)組元素刪除、拼接、添加、倒序排列等相關操作技巧,需要的朋友可以參考下
    2020-01-01
  • JSON.stringify 基礎與進階用法完全攻略

    JSON.stringify 基礎與進階用法完全攻略

    掌握JSON.stringify核心功能,包括數(shù)據(jù)類型支持、嵌套處理、循環(huán)引用檢測、格式化輸出及安全性保障,適用于API傳輸、配置生成等場景,助開發(fā)者高效處理數(shù)據(jù),本文給大家介紹JSON.stringify基礎與進階用法完全攻略,感興趣的朋友跟隨小編一起看看吧
    2025-09-09
  • layui.js實現(xiàn)的表單驗證功能示例

    layui.js實現(xiàn)的表單驗證功能示例

    這篇文章主要介紹了layui.js實現(xiàn)的表單驗證功能,結合實例形式分析了基于layui.js的事件監(jiān)聽、驗證、判定等相關操作技巧,需要的朋友可以參考下
    2017-11-11
  • webpack搭建腳手架打包TypeScript代碼

    webpack搭建腳手架打包TypeScript代碼

    本文主要介紹了webpack搭建腳手架打包TypeScript代碼,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09

最新評論

定安县| 凉城县| 广昌县| 乐都县| 遂宁市| 启东市| 正宁县| 即墨市| 德州市| 滦平县| 万宁市| 日土县| 七台河市| 屯留县| 玉林市| 诏安县| 柯坪县| 锡林郭勒盟| 平乐县| 台州市| 望奎县| 许昌县| 阳谷县| 铅山县| 义马市| 通河县| 漳浦县| 开鲁县| 柳河县| 建平县| 普洱| 永州市| 宜阳县| 莒南县| 崇明县| 三河市| 眉山市| 娄烦县| 海阳市| 得荣县| 永济市|