H5 拖放 - 学习

前言

一个典型的drag操作是这样开始的:用户用鼠标选中一个可拖动的(draggable)元素,移动鼠标到一个可放置的(droppable)元素,然后释放鼠标。 在操作期间,会触发一些事件类型,有一些事件类型可能会被多次触发

第一部分 在拖动元素上

要使其他的 HTML 元素可拖动,必须做三件事:

  • 在你想要拖动的元素上,将 draggable 属性设置成 true
  • 为事件添加一个监听器 dragstart
  • 在上面定义的监听器中设置拖动数据

1.1 draggable: 可拖动属性

  • draggable: 全局属性, 是一个枚举类型, 而不是布尔类型. 这意味着必须显式指定值为 true 或者 false ,像 <label draggable>Example Label</label> 这样的简写是不允许的。正确的用法是` <label draggable="true">Example Label</label>
  • 默认情况下,只有已选中的文本、图片、链接可以拖动。对其它的元素来说,必须按拖动机制的顺序设置 ondragstart 事件才能正常工作
  • 属性 draggable 设置为 true,所以这个元素变成可拖动的。如果该属性被省略或被设置为 false,则该元素将不会被拖动,而是只选中文本。
  • 注意,当一个元素被设置成可拖动的时候, 文本或者其中的其他元素不能再以正常的方式(通过鼠标点击和拖动)被选择。用户必须按住 alt 键,用鼠标选择文本,或者使用键盘来代替。
    <!-- 
      拖动元素, 设置全局拖动属性   
     -->
    <div class="drop-item" draggable="true"></div>

1.2 ondragstart: 开始拖动事件

  • 当用户开始拖动时,会触发 dragstart 事件(此事件会冒泡),
  • 所有拖拽事件都有一个名为 dataTransfer的属性,它持有拖曳数据(dataTransfer 是一个 DataTransfer 对象)。
  • 一个web应用需要添加拖拽功能时,应当仅仅使用DragEvent(事件对象)和DataTransfer(事件对象中的属性)接口即

DataTransfer对象

这个对象可以从所有拖动事件 drag eventsdataTransfer属性上获取,但是不能单独创建。

详情见MDN资料

  • 属性:

    • dropEffect: 影响到拖动过程中浏览器显示的鼠标样式
  • 方法:

    • setData: 设置拖动数据
    /**
           * @name: 设置拖动元素的数据
           * @param {type: 数据MIME} 
           * @param {value: 数据值} 
           * @return: none
    */
    event.dataTransfer.setData(type, value);
    
    // =============== 示例 ==============
    /*
        * type类型: 
            application/x-bookmark: 自定义类型
            text/uri-list: URL
            text/plain: 文本
        * 您可以以多种格式提供数据, 每种格式可以提供一次, 如果您试图以相同的格式添加两次数据,那么新的数据将替换旧的数据。
        * 不能设置复杂类型数据, 此时可以通过反序列化解决
    */
    var dt = event.dataTransfer;
    dt.setData("application/x-bookmark", bookmarkString);
    dt.setData("text/uri-list", "http://www.mozilla.org");
    dt.setData("text/plain", "http://www.mozilla.org");
    
    • getData(): 获取设置的数据
    /**
             * @name: 获取通过setData()设置的数据
             * @param {type: 数据MIME} 
             * @return: none
    */
    event.dataTransfer.clearData(type);
    
    /*
      * 参数必填, 没有参数会直接报错
      * 当参数的 MIME 没有对应值时, 返回空字符串
    */
    event.dataTransfer.getData("text/plain");
    
    • clearData: 清除数据**
    /**
           * @name: 清除拖动元素的数据
           * @param {type?: 数据MIME} 
           * @return: none
    */
    event.dataTransfer.clearData(type);
    
    // ================ 示例 ==================
    /*
        * 如果没有参数调用此方法,或者格式为空 ,则将删除所有类型的数据。
        * 该方法只能在dragstart 事件的处理程序中使用,因为这是拖动操作的数据存储只能写入的时间。
        * 
    */
    event.dataTransfer.clearData("text/uri-list");
    
    • setDragImage(): 设置拖动反馈图像

    当一个拖动发生时,一个半透明的图像是由拖拽目标生成的("dragstart" 事件被触发的元素),并在拖动过程中跟踪鼠标指针。这个映像是自动创建的,所以你不需要自己创建它。但是,您仍然可以使用 setDragImage() 方法来自定义拖动反馈图像。

    /**
           * @name: 设置拖动反馈图像
           * @param {image: 图像的引用, 通常是图像元素, 也可以是画布(canvas) 或任何其他元素}
           * @param {xOffset: 相对于图片的横向偏移量}
           * @param {yOffset: 相对于图片的纵向偏移量}
           * @return: none
    */
    event.dataTransfer.setDragImage(image, xOffset, yOffset);
    
    // ============== 示例 ===========
    /*
        * 引用通常是一个图像元素,但它也可以是画布(canvas)或任何其他元素。
    */
    var canvas = 
    document.createElementNS("http://www.w3.org/1999/xhtml","canvas");
    canvas.width = canvas.height = 50;
    
    var ctx = canvas.getContext("2d");
    ctx.lineWidth = 4;
    ctx.moveTo(0, 0);
    ctx.lineTo(50, 50);
    ctx.moveTo(0, 50);
    ctx.lineTo(50, 0);
    ctx.stroke();
    
    var dt = event.dataTransfer;
    dt.setData('text/plain', 'Data to Drag');
    dt.setDragImage(canvas, 25, 25);
    

第二部分 放置目标

web页面或应用程序的大多数区域都不是 drop 数据的有效位置。因此,这些事件的默认处理是不允许出现 drop。

如果您想要允许 drop,您必须通过取消事件来防止默认的处理, 在 dragover 事件中调用 preventDefault() 方法将表明在该位置允许 drop 。

1. dragover: 当元素或选中的文本被拖到一个可释放目标上时触发(每100毫秒触发一次)。

在 dragover 事件中调用 preventDefault() 方法, 但是这样的话, 会将所有拖动元素放置进来, 通常希望只在某些情况下调用 preventDefault() 方法, 这时可以通过检查拖放元素的 dataTransfer 的types属性(而使用ev.dataTransfer.getData("text/plain") 这种方式无用, 因为在dragover事件中无法获取到数据的)

  • types属性: 拖动操作中使用的数据格式数组。每种格式都是字符串类型。如果拖动操作不包含数据,则此数组列表将为空。如果拖动操作中包含任何文件,则其中一个类型将是Files。

    特别注意: 这是一个只读属性, 如果通过event.dataTransfer打印出来是查看不到数据的, 需要enent.dataTransfer.types直接查看数据

    // 可以通过判断 types 中存放的数据类型来判断是否是需要的拖动元素 (因为图片, 链接也是默认可以拖动的) -- form/item(可以自定义类型的)
        if ([...e.dataTransfer.types].includes("form/item")) {
          e.preventDefault();
        }
    

2. drop: 拖动元素放置的时候触发

处理放置效果, 在drop事件中处理, 在这个事件中可以调用event.dataTransfer.getData()来获取数据

注意每个处理程序调用 preventDefault() 来阻止对这个事件的其它处理过程(如触点事件或指针事件)

// 拖动第四步: 在拖动元素放置到 放置目标 , 执行一系列的操作
  // drop事件: 当元素或选中的文本在可释放目标上被释放时触发
  box.addEventListener("drop", function(e) {
    console.log(e.dataTransfer.getData("form/item"));
    e.preventDefault();
    this.innerText = e.dataTransfer.getData("form/item");
  });

第三部分 简单实例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
      * {
        margin: 0;
        bottom: 0;
      }
      div {
        display: inline-block;
        margin-right: 100px;
      }
      .dorp {
        width: 100px;
        height: 100px;
        background: #000;
      }
      .box {
        width: 300px;
        height: 300px;
        border: 1px solid red;
      }
      .box2 {
        width: 100%;
        height: 100%;
      }
    </style>
  </head>
  <body>
    <!-- 拖动第一步: 设置可拖动属性 -->
    <div class="dorp" draggable="true"></div>
    <div class="box">
      <div class="box2"></div>
    </div>
    <div class="dorp" draggable="true"></div>
  </body>
</html>
<script>
  let dorp = document.querySelector(".dorp");
  let box = document.querySelector(".box");
  // 拖动第二步: 开始拖动时, 设置拖动数据
  // dragstart事件: 当用户开始拖动一个元素或选中的文本时触发
  dorp.addEventListener("dragstart", e => {
    // 获取dateTransfer接口
    let dt = e.dataTransfer;
    // 设置拖动过程中数据 -- 可以自定义的
    dt.setData("form/item", "这可能是一个下拉框选项");
  });

  // 拖动第三步: 在放置目标上处理dragover事件 ==> 阻止默认事件(可以根据数据类型不同来判断是否可以放置)
  // dragover事件: 当元素或选中的文本被拖到一个可释放目标上时触发(每100毫秒触发一次)。
  box.addEventListener("dragover", e => {
    // 可以通过判断 types 中存放的数据类型来判断是否是需要的拖动元素 (因为图片, 链接也是默认可以拖动的)
    if ([...e.dataTransfer.types].includes("form/item")) {
      e.preventDefault();
    }
  });
  // 拖动第四步: 在拖动元素放置到 放置目标 , 执行一系列的操作
  // drop事件: 当元素或选中的文本在可释放目标上被释放时触发
  box.addEventListener("drop", function(e) {
    console.log(e.dataTransfer.getData("form/item"));
    e.preventDefault();
    this.innerText = e.dataTransfer.getData("form/item");
  });
</script>

最后

只是简单学习了一下H5的拖放过程, 没有深入的细究, 还有好多事件 和 属性在一定的场景下有作用的

参考资料

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,468评论 5 473
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,620评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,427评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,160评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,197评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,334评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,775评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,444评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,628评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,459评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,508评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,210评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,767评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,850评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,076评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,627评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,196评论 2 341

推荐阅读更多精彩内容