拖拽小记

157 阅读4分钟

引文

我们的产品说: “ -- 这块我想做成拖拽的“

“ -- 好“

我们使用了sortTable.jssortablejs.github.io/Sortable/#s…,然后实现了一个这样的结果:

image.png

实现原理

const lista1 = document.getElementById('lista1');
const lista2 = document.getElementById('lista2');

Sortable.create(lista1, {
    animation: 150,
    chosenClass: "seleccionado",
    // ghostClass: "fantasma"
    dragClass: "drag",

    onEnd: () => {
        console.log('Se inserto un elemento');
    },
    group: "lista-personas",
    store: {
        // Guardamos el orden de la lista
        set: (sortable) => {
            const orden = sortable.toArray();
            localStorage.setItem(sortable.options.group.name, orden.join('|'));
        },

        // Obtenemos el orden de la lista
        get: (sortable) => {
            const orden = localStorage.getItem(sortable.options.group.name);
            return orden ? orden.split('|') : [];
        }
    }
});

Sortable.create(lista2, {
    animation: 150,
    chosenClass: "seleccionado",
    // ghostClass: "fantasma"
    dragClass: "drag",

    onEnd: () => {
        console.log('Se inserto un elemento');
    },
    group: "lista-personas",
    store: {
        // Guardamos el orden de la lista
        set: (sortable) => {
            const orden = sortable.toArray();
            localStorage.setItem(sortable.options.group.name, orden.join('|'));
        },

        // Obtenemos el orden de la lista
        get: (sortable) => {
            const orden = localStorage.getItem(sortable.options.group.name);
            return orden ? orden.split('|') : [];
        }
    }
});

常用的参数

// 动画的时间
animation: 200, 

// 不需要进行拖动的元素
filter: ".ignore-elements" 

// true时sortable对象不能拖放排序等功能,为false时为可以进行排序,相当于一个开关
disabled: !this.editable 

// 被选中项的css 类名
chosenClass: "seleccionado"

// 正在被拖拽中的css类名
dragClass: "drag",

常用的方法

// 元素被选中
onChoose: function (/**Event*/evt) {
    evt.oldIndex;  // element index within parent
},

// 开始拖拽的时候
onStart: function (/**Event*/evt) {
    evt.oldIndex;  // element index within parent
},

// 结束拖拽
onEnd: function (/**Event*/evt) {
},

// 列表内元素顺序更新的时候触发
onUpdate: function (/**Event*/evt) {
    // same properties as onEnd
},
// 列表的任何更改都会触发
onSort: function (/**Event*/evt) {
    // same properties as onEnd
},

 // 拖拽元素改变位置的时候
onChange: function(/**Event*/evt) {
    evt.newIndex // most likely why this event is used is to get the dragging element's current index
    // same properties as onEnd
}

文档很简洁,使用起来也很简单。但sortTable只是在当前的页面中能进行拖拽,如果,我想从这个浏览器窗口,拖动到另外一个浏览器窗口呢?

于是,发现了更有意思的东西。。。

transmat

跨越浏览器的边界,实现数据共享 前提 -- 浏览器版本:Chrome 91.0.4472.114

介绍

Transmat 是一个围绕 DataTransfer API 的小型库 ,它使用 drag-drop 和 copy-paste 交互简化了在 Web 应用程序中传输和接收数据的过程。

DataTransfer 对象用于保存拖动并放下(drag and drop)过程中的数据。它可以保存一项或多项数据,这些数据项可以是一种或者多种数据类型。

DataTransfer API 能够将多种不同类型的数据传输到用户设备上的其他应用程序,该 API 所支持的数据类型,常见的有这几种:text/plain、text/html 和 application/json 等。

实现原理

from

  1. div#source 元素添加了 draggable 属性,该属性用于标识元素是否允许被拖动,它的取值为 truefalse
<div id="source" draggable="true" tabindex="0">今天是我分享</div>
  1. 利用 transmat 这个库提供的 addListeners 函数div#source 元素,添加了 transmit 的事件监听
  2. 在对应的事件处理器中,我们先创建了 Transmat 对象,然后调用该对象上的 setData 方法设置不同 MIME 类型的数据。

媒体类型(通常称为 Multipurpose Internet Mail Extensions 或 MIME 类型 )是一种标准,用来表示文档、文件或字节流的性质和格式。

  • text/plain:表示文本文件的默认值,一个文本文件应当是人类可读的,并且不包含二进制数据。
  • text/html:表示 HTML 文件类型,一些富文本编辑器会优先从 dataTransfer 对象上获取text/html 类型的数据,如果不存在的话,再获取 text/plain 类型的数据。
  • text/uri-list:表示 URI 链接类型,大多数浏览器都会优先读取该类型的数据,如果发现是合法的 URI 链接,则会直接打开该链接。如果不是的合法 URI 链接,对于 Chrome 浏览器来说,它会读取。
  • text/plain 类型的数据并以该数据作为关键词进行内容检索。
  • application/json:表示 JSON 类型,该类型对前端开发者来说,应该都比较熟悉了。
<script>
  const { Transmat, addListeners, TransmatObserver } = transmat;

  const source = document.getElementById("source");

  addListeners(source, "transmit", (event) => {
    const transmat = new Transmat(event);

    transmat.setData({
      "text/plain": "今天是我分享",
      "text/html": `<h1>今天是我分享</h1>`,

      "text/uri-list": "www.baidu.com",

      "application/json": {
        name: "分享会",
        wechat: "semlinker",
      },
    });
  });
</script>

to

<div id="target" tabindex="0">放这里</div>

我们利用 transmat 这个库提供的 addListeners 函数为 div#target 元素,添加了 receive 的事件监听。顾名思义,该 receive 事件表示接收消息

在对应的事件处理器中,我们通过 transmat 对象的 hasType 方法过滤了 application/json 的消息,然后通过 JSON.parse 方法进行反序列化获得对应的数据,同时把对应 jsonString 的内容显示在 div#target 元素内。

const { Transmat, addListeners, TransmatObserver } = transmat;

const target = document.getElementById("target");

addListeners(target, "receive", (event) => {
  const transmat = new Transmat(event);
  // 判断是否含有"application/json"类型的数据
  // 及事件类型是否为drop或paste事件
  if (transmat.hasType("application/json") 
    && transmat.accept()
  ) {
    const jsonString = transmat.getData("application/json");
    const data = JSON.parse(jsonString);
    target.textContent = jsonString;
  }
});

代码链接github.com/didididiboo…