
前端UI組件【免費(fèi)下載鏈接】SortableReorderable drag-and-drop lists for modern browsers and touch devices. No jQuery or framework required.項(xiàng)目地址https://gitcode.com/gh_mirrors/so/Sortable點(diǎn)擊查看免費(fèi)下載導(dǎo)讀MultiDrag 是 Sortable.js 官方插件體系中的一員它讓用戶能夠在同一個(gè) Sortable 容器內(nèi)一次選中多個(gè)條目并作為一個(gè)整體進(jìn)行拖拽放置完成后這些條目會(huì)在新位置按各自原有的順序依次展開(kāi)、緊挨在一起排列。本文以 plugins/MultiDrag/README.md 為骨架結(jié)合 plugins/MultiDrag/MultiDrag.js 的源碼實(shí)現(xiàn)完整講解插件的掛載方式、全部配置項(xiàng)、事件對(duì)象屬性、Sortable.utils編程式 API以及“折疊fold/展開(kāi)unfold”動(dòng)畫(huà)與修飾鍵范圍選擇的底層原理幫助你直接落地一個(gè)可復(fù)用的多選拖拽列表。MultiDrag 插件能做什么在未啟用 MultiDrag 時(shí)Sortable 一次只能拖拽單個(gè)條目啟用后你可以單擊條目將其選中高亮再單擊已選中條目則取消選中按住multiDragKey指定的修飾鍵如CTRL點(diǎn)擊可多選不相鄰的條目在按住修飾鍵的情況下連續(xù)點(diǎn)擊兩個(gè)條目可區(qū)間選中二者之間的全部可拖拽條目源碼中借助evt.shiftKey與lastMultiDragSelect實(shí)現(xiàn)見(jiàn) MultiDrag.js拖動(dòng)任意一個(gè)已選中條目整個(gè)選中組跟隨移動(dòng)松開(kāi)后所有條目在插入位置按原始順序展開(kāi)排列并通過(guò)動(dòng)畫(huà)呈現(xiàn)“折疊—展開(kāi)”效果選中的一組條目可以被拖入另一個(gè) Sortable 實(shí)例跨列表移動(dòng)也可以配合clone拖放模式實(shí)現(xiàn)復(fù)制。插件內(nèi)置了兩個(gè)全局可見(jiàn)的輔助類(lèi)名sortable-selected選中樣式與sortable-chosen拖拽中樣式前者可通過(guò)selectedClass選項(xiàng)覆蓋。安裝與掛載在模塊化構(gòu)建中手動(dòng)掛載MultiDrag 與 Swap 一樣屬于需要顯式掛載的插件。以 ES Module 方式使用import { Sortable, MultiDrag } from sortablejs; Sortable.mount(new MultiDrag());掛載動(dòng)作發(fā)生在創(chuàng)建 Sortable 實(shí)例之前。Sortable.mount()由 src/PluginManager.js 實(shí)現(xiàn)其核心職責(zé)有兩個(gè)為插件補(bǔ)齊靜態(tài)默認(rèn)屬性initializeByDefault: true校驗(yàn)插件名pluginName唯一重復(fù)掛載同名插件會(huì)拋出Sortable: Cannot mount plugin ... more than once錯(cuò)誤。MultiDrag 的pluginName為multiDrag見(jiàn) MultiDrag.js。插件掛載后initializePlugins會(huì)在實(shí)例化時(shí)檢查sortable.options.multiDrag只有該項(xiàng)為真時(shí)才初始化插件實(shí)例并掛到sortable.multiDrag上見(jiàn) PluginManager.js。使用完整版構(gòu)建已內(nèi)置掛載如果不希望手動(dòng)掛載可以直接使用倉(cāng)庫(kù)根目錄的 Sortable.js完整版或使用入口文件 entry/entry-complete.js。該入口的完整邏輯如下import Sortable from ./entry-defaults.js; import Swap from ../plugins/Swap; import MultiDrag from ../plugins/MultiDrag; Sortable.mount(new Swap()); Sortable.mount(new MultiDrag()); export default Sortable;也就是說(shuō)完整版構(gòu)建在導(dǎo)入階段就已經(jīng)把 MultiDrag 與 Swap 兩個(gè)插件掛載完畢之后只需要在實(shí)例選項(xiàng)中開(kāi)啟multiDrag: true即可使用。完整配置選項(xiàng)在實(shí)例化時(shí)傳入如下選項(xiàng)即可開(kāi)啟并定制多選拖拽new Sortable(el, { multiDrag: true, // Enable the plugin selectedClass: sortable-selected, // Class name for selected item multiDragKey: null, // Key that must be down for items to be selected avoidImplicitDeselect: false, // true - if you dont want to deselect items on outside click // Called when an item is selected onSelect: function(/**Event*/evt) { evt.item // The selected item }, // Called when an item is deselected onDeselect: function(/**Event*/evt) { evt.item // The deselected item } });各選項(xiàng)的作用與默認(rèn)值如下表選項(xiàng)默認(rèn)值說(shuō)明multiDrag無(wú)默認(rèn)關(guān)閉是否啟用 MultiDrag 插件true開(kāi)啟selectedClasssortable-selected選中條目的樣式類(lèi)名multiDragKeynull進(jìn)行多選時(shí)需按住的按鍵null表示無(wú)需按鍵avoidImplicitDeselectfalse為true時(shí)點(diǎn)擊容器外部不會(huì)隱式取消全部選中onSelect無(wú)條目被選中時(shí)的回調(diào)evt.item為被選中的元素onDeselect無(wú)條目被取消選中時(shí)的回調(diào)evt.item為被取消的元素說(shuō)明multiDrag選項(xiàng)本身不在插件的defaults中插件是否初始化由PluginManager依據(jù)sortable.options.multiDrag判定PluginManager.js。而selectedClass、multiDragKey、avoidImplicitDeselect以及一個(gè)自定義的setData用于把選中組文本寫(xiě)入dataTransfer都在插件的defaults中注冊(cè)MultiDrag.js因此即使不顯式傳入也會(huì)獲得默認(rèn)值。setData默認(rèn)行為值得一提的細(xì)節(jié)插件默認(rèn)重寫(xiě)了setData。當(dāng)拖拽的是多選組時(shí)會(huì)把組內(nèi)各元素的textContent以, 拼接后寫(xiě)入剪貼板數(shù)據(jù)dataTransfer.setData(Text, data)否則回退為單個(gè)dragEl.textContent見(jiàn) MultiDrag.js。這意味著把多選組拖出瀏覽器時(shí)粘貼得到的是多個(gè)條目的文本。multiDragKey選項(xiàng)詳解multiDragKey指定“進(jìn)行多選時(shí)需按住的按鍵”默認(rèn)null表示不需要任何按鍵單擊即可選中/取消。對(duì)于特殊按鍵直接使用其按鍵名即可例如CTRL鍵寫(xiě)作CTRL大小寫(xiě)不敏感Sortable.create(list, { multiDrag: true, multiDragKey: CTRL // 按住 Ctrl 才能多選其余按鍵無(wú)效 });源碼中通過(guò)optionListeners.multiDragKey對(duì)傳入值做了歸一化處理MultiDrag.js統(tǒng)一轉(zhuǎn)為小寫(xiě)后判斷ctrl會(huì)被映射為 DOM 事件中的標(biāo)準(zhǔn)按鍵名Control長(zhǎng)度大于 1 的按鍵名會(huì)被轉(zhuǎn)換為首字母大寫(xiě)如shift→Shift、alt→Alt歸一化后的值用于與keydown/keyup事件的evt.key比對(duì)從而維護(hù)multiDragKeyDown標(biāo)志MultiDrag.js。因此以下寫(xiě)法都是等價(jià)的CTRL、ctrl、Control、control。邊界行為若設(shè)置了multiDragKey但拖拽時(shí)并未按住該鍵drop處理會(huì)先執(zhí)行_deselectMultiDrag()清空選中再繼續(xù)MultiDrag.js保證“無(wú)修飾鍵 單條拖拽”的預(yù)期。SHIFT 區(qū)間選中的實(shí)現(xiàn)當(dāng)multiDragKey處于按下?tīng)顟B(tài)evt.shiftKey為真且此前已有l(wèi)astMultiDragSelect記錄時(shí)點(diǎn)擊另一個(gè)條目會(huì)觸發(fā)區(qū)間選擇從上次選中的索引到本次點(diǎn)擊索引之間的所有可拖拽、未被filter過(guò)濾的元素都會(huì)被一并選中MultiDrag.js。源碼中依次檢查了options.draggable匹配與options.filter支持函數(shù)或逗號(hào)分隔的選擇器字符串確保區(qū)間內(nèi)只納入真正可拖拽的條目。selectedClass選項(xiàng)詳解selectedClass是選中條目的樣式類(lèi)名默認(rèn)sortable-selected。當(dāng)用戶點(diǎn)擊選中條目時(shí)該類(lèi)會(huì)被toggleClass動(dòng)態(tài)添加到元素上選中時(shí)加上、取消時(shí)移除。自定義樣式與類(lèi)名示例.selected { background-color: #f9c7c8; border: solid red 1px; }Sortable.create(list, { multiDrag: true, selectedClass: selected });在 tests/style.css 等測(cè)試樣式中也可見(jiàn)到.sortable-selected的用法你可以把它作為默認(rèn)類(lèi)名的樣式參考。選中類(lèi)同樣會(huì)作用于編程式選中Sortable.utils.select并用于“排除選中項(xiàng)”的索引計(jì)算例如index(dragEl, :not(. this.options.selectedClass ))見(jiàn) MultiDrag.js。事件對(duì)象屬性當(dāng)select/deselect/ 拖拽相關(guān)事件如drag、drop、add、update觸發(fā)時(shí)事件對(duì)象evt上會(huì)附加以下 MultiDrag 專(zhuān)屬屬性items: HTMLElement[]— 當(dāng)前選中的條目數(shù)組未選中時(shí)為空數(shù)組clones: HTMLElement[]— 當(dāng)前生成的克隆元素?cái)?shù)組未克隆時(shí)為空數(shù)組oldIndicies: Index[]— 選中各元素在拖拽前的索引信息數(shù)組newIndicies: Index[]— 選中各元素在拖拽后的索引信息數(shù)組。這些屬性由插件的靜態(tài)方法eventProperties()統(tǒng)一組裝MultiDrag.js并由 PluginManager.js 的getEventProperties()合并到每一個(gè)派發(fā)的事件對(duì)象上因此你在onSelect、onDeselect、onDrop等任意回調(diào)中都能讀取它們。Index 對(duì)象結(jié)構(gòu)每個(gè)Index對(duì)象包含兩個(gè)字段element: HTMLElement— 被計(jì)算索引的元素本身index: Number— 該元素在父容器中的索引。關(guān)于newIndicies的重要說(shuō)明在排序過(guò)程中觸發(fā)的任何事件里非主拖拽元素的newIndicies索引會(huì)被記為-1。原因是這些元素要么已被臨時(shí)移出 DOM折疊動(dòng)畫(huà)期間被移除要么正處于“折疊到主拖拽元素”的動(dòng)畫(huà)中動(dòng)畫(huà)結(jié)束后才會(huì)被移除見(jiàn) MultiDrag.js。這一行為對(duì)業(yè)務(wù)判斷非常關(guān)鍵如果你想在onMove、onDrag等回調(diào)中讀取“選中組最終落點(diǎn)”應(yīng)以主拖拽元素evt.dragEl的索引為基準(zhǔn)或等到onEnd之后再讀取newIndicies。事件監(jiān)聽(tīng)示例new Sortable(list, { multiDrag: true, onSelect(evt) { console.log(選中:, evt.item, 當(dāng)前選中數(shù):, evt.items.length); }, onDeselect(evt) { console.log(取消選中:, evt.item, 剩余選中數(shù):, evt.items.length); }, onEnd(evt) { console.log(拖拽結(jié)束新索引:, evt.newIndicies); } });Sortable.utils 編程式 API除了交互式點(diǎn)擊選擇MultiDrag 還暴露了兩個(gè)靜態(tài)工具方法用于以編程方式控制選中狀態(tài)MultiDrag.jsselect(el: HTMLElement)— 選中給定的多選條目。內(nèi)部會(huì)校驗(yàn)父容器已啟用multiDrag、元素未被選中若此前有其他 Sortable 的選中組先隱式清空舊組再切換歸屬最后添加selectedClass并壓入選中數(shù)組。deselect(el: HTMLElement)— 取消選中給定的多選條目。同樣會(huì)校驗(yàn)父容器配置與元素是否在選中數(shù)組中隨后移除selectedClass并移出選中數(shù)組。import { Sortable } from sortablejs; const s new Sortable(document.getElementById(list), { multiDrag: true }); // 編程式選中前三個(gè)條目 [li[data-id1], li[data-id2], li[data-id3]] .map(sel s.el.querySelector(sel)) .forEach(el Sortable.utils.select(el)); // 取消選中某一個(gè) Sortable.utils.deselect(document.querySelector(li[data-id2]));注意Sortable.utils是靜態(tài)命名空間select/deselect直接掛在MultiDrag插件的utils上MultiDrag.js使用前需確認(rèn)插件已mount且目標(biāo)實(shí)例開(kāi)啟了multiDrag。編程式選中不會(huì)觸發(fā)onSelect回調(diào)該方法僅操作類(lèi)名與內(nèi)部數(shù)組如需聯(lián)動(dòng)業(yè)務(wù)邏輯請(qǐng)自行處理。源碼級(jí)原理折疊Fold與展開(kāi)UnfoldMultiDrag 最核心的視覺(jué)機(jī)制是“折疊—展開(kāi)”。理解它有助于排查動(dòng)畫(huà)、索引相關(guān)的疑難問(wèn)題拖拽開(kāi)始dragStarted若啟用了animation除主拖拽元素外的所有選中元素先被設(shè)為position: absolute并統(tǒng)一套用主拖拽元素的矩形setRect使它們視覺(jué)上“折疊”到拖拽元素上同時(shí)置folding trueMultiDrag.js。拖拽中dragOverCompleted當(dāng)拖拽進(jìn)入可放置位置且選中數(shù)大于 1 時(shí)再次把所有選中元素折疊到dragEl的絕對(duì)矩形并移到父容器末尾避免干擾克隆元素的插入MultiDrag.js。放置dropremoveMultiDragElements()先將非主元素全部移出 DOM再按multiDragIndex依次insertBefore到插入點(diǎn)之后形成“緊挨排列”隨后toSortable.captureAnimationState()與animateAll()驅(qū)動(dòng)展開(kāi)動(dòng)畫(huà)MultiDrag.js。還原revert若拖拽被還原到原位置源碼會(huì)為所有選中元素建立展開(kāi)動(dòng)畫(huà)狀態(tài)并重新插入MultiDrag.js。索引影響展開(kāi)過(guò)程中元素位置相對(duì)主拖拽元素發(fā)生偏移即使dragEl的索引未變選中組整體也可能移動(dòng)。為此drop末尾會(huì)檢測(cè)各元素sortableIndex與當(dāng)前索引是否一致若不一致則手動(dòng)補(bǔ)發(fā)update與sort事件MultiDrag.js。這意味著在onEnd中讀取newIndicies比在onUpdate中更可靠。隱式取消選中Implicit Deselect默認(rèn)情況下avoidImplicitDeselect: false點(diǎn)擊容器外部或非拖拽條目會(huì)清空當(dāng)前所有選中。源碼中的_deselectMultiDragMultiDrag.js在pointerup/mouseup/touchend上觸發(fā)并做了三重守衛(wèi)拖拽已開(kāi)始則不處理、選中組不屬于當(dāng)前 Sortable 則不處理、目標(biāo)是容器內(nèi)可拖拽條目則不處理保證點(diǎn)擊條目本身不會(huì)誤清空。將avoidImplicitDeselect: true可關(guān)閉該行為保留選中狀態(tài)直至用戶主動(dòng)取消??缌斜硗献c克隆模式多選組同樣支持跨 Sortable 實(shí)例拖拽const listA Sortable.create(document.getElementById(list-a), { multiDrag: true }); const listB Sortable.create(document.getElementById(list-b), { multiDrag: true, group: shared, sort: false });拖入sort: false的列表時(shí)源碼會(huì)在折疊動(dòng)畫(huà)后移除原列表的輔助元素并在目標(biāo)列表展示克隆組MultiDrag.js若配合group: { name: shared, pull: clone }克隆元素會(huì)隨拖拽組一并插入目標(biāo)列表insertMultiDragClones見(jiàn) MultiDrag.js實(shí)現(xiàn)“多選復(fù)制”跨列表放置后multiDragSortable會(huì)被更新為新的目標(biāo) SortableMultiDrag.js后續(xù)在新容器中的點(diǎn)擊選中邏輯以新容器為準(zhǔn)??焖賹?shí)踐清單使用完整版構(gòu)建根目錄 Sortable.js或自行Sortable.mount(new MultiDrag())創(chuàng)建實(shí)例時(shí)設(shè)置multiDrag: true按需配置selectedClass配合 CSS 高亮樣式與multiDragKey如CTRL通過(guò)onSelect/onDeselect回調(diào)或事件對(duì)象的items/newIndicies屬性聯(lián)動(dòng)業(yè)務(wù)邏輯需要代碼控制選中時(shí)使用Sortable.utils.select(el)/Sortable.utils.deselect(el)關(guān)注avoidImplicitDeselect對(duì)“點(diǎn)擊外部是否清空選中”的影響。以上配置與行為均可在倉(cāng)庫(kù)源碼 plugins/MultiDrag/MultiDrag.js 與插件掛載機(jī)制 src/PluginManager.js 中逐一印證測(cè)試目錄 tests/ 中的 HTML 用例如 single-list.html、dual-list.html可作為搭建多選拖拽頁(yè)面的起步模板。贊分享前端UI組件【免費(fèi)下載鏈接】SortableReorderable drag-and-drop lists for modern browsers and touch devices. No jQuery or framework required.項(xiàng)目地址https://gitcode.com/gh_mirrors/so/Sortable點(diǎn)擊查看免費(fèi)下載相關(guān)推薦react-map-gl 可拖拽 Marker 實(shí)戰(zhàn)draggable 屬性與拖拽事件的完整鏈路react map gl 可拖拽 Marker 實(shí)戰(zhàn)draggable 屬性與拖拽事件的完整鏈路 本篇指南基于 react map gl 倉(cāng)庫(kù)中的 examp前端UI組件Vuetify VFileUpload 組件完整實(shí)戰(zhàn)指南拖拽上傳、自定義插槽與源碼原理解析Vuetify VFileUpload 組件完整實(shí)戰(zhàn)指南拖拽上傳、自定義插槽與源碼原理解析 導(dǎo)讀 v file upload 是 Vuetify 中面向文件上前端UI組件Sortable拖拽限制containment屬性使用指南Sortable拖拽限制containment屬性使用指南 在Web應(yīng)用開(kāi)發(fā)中拖拽功能是提升用戶體驗(yàn)的重要交互方式。Sortable作為一款輕量級(jí)拖拽庫(kù)提前端UI組件上一篇ClawX 會(huì)話標(biāo)題修復(fù)讓 ACP 截?cái)嗟墓ぷ髂夸浶欧獠辉俪蔀橹鲿?huì)話標(biāo)題下一篇魔獸爭(zhēng)霸3輔助工具終極指南5分鐘快速上手教程創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考