HTML5 拖放(Drag 和 Drop)html排序拖拽案例,vue仿照element穿梭框拖拽案例。

(125) 2024-05-02 09:01:01

HTML5 拖放(Drag 和 Drop)html排序拖拽案例,vue仿照element穿梭框拖拽案例。 (https://mushiming.com/)  第1张

vue版本 

 HTML5 拖放(Drag 和 Drop)html排序拖拽案例,vue仿照element穿梭框拖拽案例。 (https://mushiming.com/)  第2张

drag常见事件

hTMl 5 拖拽 使用拖拽需要在元素标签上设置 draggable = true

dragover 在可拖动的元素或者被选择的文本被拖进一个有效的放置目标后间隔几百毫秒持续进行触发 (简单来说就是拖拽到指定的目标元素就会就行持续触发,一般绑定到需要拖拽元素最外层的盒子)

dragstart 事件在用户开始拖动元素或被选择的文本时调用

dragleave 事件在拖动的元素或选中的文本离开一个有效的放置目标时被触发

dragend 事件在拖放操作结束时触发(通过释放鼠标按钮或单击 escape 键)。

drop 事件在元素或选中的文本被放置在有效的放置目标上时被触发

drag 事件在用户拖动元素或选择的文本时,每隔几百毫秒就会被触发一次。

dragenter 事件在可拖动的元素或者被选择的文本进入一个有效的放置目标时触发

html5 排序拖拽 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>拖拽</title>
</head>

<style>
    *{
        margin: 0;
        padding: 0;
    }
    .list{
        width: 400px;
        height: 500px;
        padding: 10px;
         background-color: #ccc;
    }
    .list p {
        width: 100%;
        height: 40px;
        margin: 10px 0 10px 0;
        text-align: center;
        line-height: 40px;
        background-color: #fff;
        box-shadow: 2px  3px 4px #fff;
    }
    li{
        list-style: none;
    }
    .dragging{
        opacity: 0;
    }
    .drag-body {
        display: flex;
        justify-content: space-around;
    }
</style>
<body class="grid_line">
<div class="drag-body">
  <div class="list" style="margin-top: 20px">
  </div>
</div>
</body>
<script>

    ;(()=> {
        let listData = [1,2,4,5,6]

        const init = () =>{
            render()// 渲染dom函数
            bindEvent() // 绑定所有dom事件处理函数

        }

        function render() {
            let list = document.querySelector('.list')
            list.appendChild(renderList())
        }

        function bindEvent() {
           let dragItems = document.querySelectorAll('.dragItem')
            let dragList = document.querySelectorAll('.dragList')
            dragList[0].addEventListener('dragover',dragoverListHandle,false)
            window.addEventListener('dragover',(e) => e.preventDefault(),false)
            dragItems.forEach(ele => {
                ele.addEventListener('dragstart',dragstartHandle,false)
                ele.addEventListener('dragend',dragendHandle,false)
            })
        }


        // 拖拽函数处理
        function dragstartHandle (ev) {
          // 拖拽开始异步执行隐藏元素
          setTimeout(() =>{this.classList.add('dragging')},0)
          // // 你拖拽了哪个元素就给哪个元素设置一个唯一的标识 可以通过 getData 拿到设置标识的元素。
          if(ev.dataeTransfer)  ev.dataeTransfer.setData("Text",ev.target.id);
        }

        function dragendHandle() {
            this.classList.remove('dragging')
        }

        function dragoverListHandle(e){
             e.preventDefault()
             // 根据类名获取当前拖债的元素。
            const dragging = this.querySelector('.dragging')
             // 获取所有非包含当前拖债的元素
            const dragItems = this.querySelectorAll('.dragItem:not(.dragging)')
            /*
            * clientY 事件属性返回当事件被触发时鼠标指针向对于浏览器页面(客户区)的垂直坐标。 客户区指的是当前窗口。
            * offsetTop 返回一个整数,表示该元素的顶部偏移量,单位是像素 px。
            *  offsetHeight 属性是一个只读属性,它返回该元素的像素高度,高度包含内边距(padding)和边框(border),不包含外边距(margin),是一个整数,单位是像素 px。
            * */
            // 假设盒子向上拖拽 e.clientY 获取当前的盒子里面鼠标 y 轴方向移动的位置,当移动的位置小于等于了上一个元素y轴方向的位置(item.offsetTop + item.offsetHeight / 2),交换dom位置
            // 核心逻辑就是拖拽的元素的位置小于等于了上一个元素底部的位置就交换位置
            const dragItme = [...dragItems].find(item => {
               return  e.clientY <= item.offsetTop + item.offsetHeight / 2
            })
            this.insertBefore(dragging,dragItme)
        }

       // 渲染自定义的ui > li
        function renderList( ) {
            let ul = document.createElement('ul')
            ul.className = 'dragList'
            listData.forEach((item,index) => {
                let li = document.createElement('li')
                li.className = 'dragItem'
                li.draggable = true
                li.innerHTML = `<p>${item}</p>`
                li.id = index
                ul.appendChild(li)
            })
            return ul
        }

        init() // 初始化调用函数
    })()

</script>
</html>

vue仿照element穿梭框支持拖拽功能

<template>
  <el-dialog
    :title="title"
    :visible.sync="visible"
    width="50%"
    :before-close="handleClose">
    <div id="box">
      <!----start 左侧隐藏区 --start-->
      <div class="box">
        <div class="title">
            <span
            >切换字段</span>
          <span>{
  
  {leftList.length}}</span>
        </div>
        <div class="search_parent">
          <input class="search_input"
                 type="text"  placeholder="请输入搜索内容"
                 @input="leftInputChange($event)"
          >
        </div>
        <!--@drop拖放事件 我们需要对被拖放元素添加@dragover.prevent来阻止浏览器执行与事件关联的默认动作. -->
        <div  class="left" @dragover.prevent  @drop="dropHandle($event,2)" >
          <!-- draggable Boolean值
          @dragstart:拖拽开始事件,可绑定于被拖拽元素上
          @dragend:拖拽结束事件,可绑定于被拖拽元素上 -->
          <div
            class="hover-color"
            v-for="(item,index) in leftList"
            :key="item.id"
            @click="leftListHandle(item,index)"
            :class="{active:leftIndexs.includes(item.id)}"
            draggable="true"
            @dragstart="dragStartHandler($event,item)"
            @dblclick="dbHandler(item,'left')"
          >{
  
  {item.name}}
          </div>
        </div>
      </div>
      <!----end 左侧隐藏区 --end-->
      <div class="middle">
        <div  :class="{active:rightIndex.length > 0 && rightList.length > 0 }" @click="btnSendHandler('left')"><i
          class="el-icon-arrow-left"
        ></i></div>
        <div :class="{active:leftIndexs.length > 0 && leftList.length > 0 }"  @click="btnSendHandler('right')"><i
          class="el-icon-arrow-right"
        ></i></div>
      </div>
      <!----start 右侧显示区 --start-->
      <div class="box">
        <div class="title">
            <span
            >显示字段</span>
          <span>{
  
  {rightList.length}}</span>
        </div>

        <div class="search_parent">
          <input class="search_input"
                 type="text"
                 placeholder="请输入搜索内容"
                 @input="rightInputChange($event)"
          >
        </div>
        <!-- 我们需要对被拖放元素添加@dragover.prevent来阻止浏览器执行与事件关联的默认动作. -->
        <div class="right"  @dragover.prevent @drop="dropHandle($event,1)">
          <div
            v-for="(item,index) in rightList"
            :key="item.id"
            @click="rightListHandle(item,index)"
            :class="{active:rightIndex.includes(item.id)}"
            class="hover-color"
            draggable="true"
            @dragstart="dragStartHandler($event,item)"
            @dblclick="dbHandler(item,'right')"
          >{
  
  {item.name}}
          </div>
        </div>
      </div>
      <!----end 右侧显示区 --end-->
    </div>
    <!-- footer -->
    <span slot="footer" class="dialog-footer">
    <el-button @click="handleClose">取 消</el-button>
    <el-button type="primary" @click="saveHandle"> 保存 </el-button>
  </span>
  </el-dialog>
</template>
<script>

export default {
  data() {
    return{
      leftList:[{name:'小东瓜',id:55456},{name:'Hello word',id:254},{name:'小樱桃',id:266}],
      rightList:[ {name:'大西瓜',id:332643},{name:'小葡萄',id:56434},],
      leftIndexs:[],
      rightIndex:[],
      cloneLeftList:[],
      cloneRightList:[],
      isDown:false,
      cacheIndex:[],
    }
  },
  props:{
    visible:false,
    title:''
  },
  created() {
    this.cloneData()
  },
  methods:{
    handleClose() {
      this.$emit('onClose')
    },
    // 左侧列表点击
    leftListHandle(item,index) {
      this.leftIndexs = [item.id]
      this.rightIndex = []
    },
    // rightBtnSend 右侧按钮传送
    btnSendHandler(type) {
      // 处理数据拼接和增删改查
      if(type == 'left') {
        if(!this.rightList.length || !this.rightIndex.length)return
        let checkedList = this.rightList.find(item => this.rightIndex.includes(item.id))
        this.leftList = this.leftList.concat([checkedList])
        this.rightList = this.rightList.filter(item => !this.rightIndex.includes(item.id))
        // 克隆的的数据处理
        this.cloneLeftList = this.cloneLeftList.concat([checkedList])
        this.cloneRightList = this.cloneRightList.filter(item => !this.rightIndex.includes(item.id))
        this.rightIndex = []
      }else if(type == 'right') {
        if(!this.leftList.length || !this.leftIndexs.length) return
        let checkedList = this.leftList.find(item => this.leftIndexs.includes(item.id))
        this.rightList = this.rightList.concat([checkedList])
        this.leftList = this.leftList.filter(item => !this.leftIndexs.includes(item.id))
        this.cloneRightList = this.cloneRightList.concat([checkedList])
        this.cloneLeftList = this.cloneLeftList.filter(item => !this.leftIndexs.includes(item.id))
        this.leftIndexs = []
      }
     },

    cloneData() {
      this.cloneLeftList = JSON.parse(JSON.stringify(this.leftList))
      this.cloneRightList = JSON.parse(JSON.stringify(this.rightList))
    },


    // 左侧双击
    dbHandler(item,type) {
      if(type == 'left') {
        this.leftListHandle(item)
        this.btnSendHandler('right')
      }else if(type == 'right') {
        this.rightListHandle(item)
        this.btnSendHandler('left')
      }
    },




    // 右侧列表点击
    rightListHandle(item,index) {
      this.rightIndex = [item.id]
      this.leftIndexs = []
    },


    // 保存

    saveHandle() {
      this.$emit('onConfirm',this.rightList)
    },

    // 拖拽设置id
    dragStartHandler(ev,item) {
      ev.dataTransfer.setData("id",item.id);
    },

    // 组织默认时间
    allowDrop(ev) {
      ev.preventDefault()
    },


    // 数据需要拖到哪个盒子就给那个盒子绑定 drop 事件,当你拖到指定的盒子就会触发 drop 事件。
    dropHandle(ev,type) {
      let id = ev.dataTransfer.getData("id");
      if(type === 1) {
        let checkedList = this.leftList.find(item => item.id == id)
        this.rightList = this.rightList.concat([checkedList])
        this.leftList =  this.leftList.filter(item => item.id != id)


        this.cloneRightList = this.cloneRightList.concat([checkedList])
        this.cloneLeftList = this.cloneLeftList.filter(item => item.id != id)
      }else {
        let checkedList = this.rightList.find(item => item.id == id)
        this.leftList = this.leftList.concat([checkedList])
        this.rightList =  this.rightList.filter(item => item.id != id)

        this.cloneLeftList = this.cloneLeftList.concat([checkedList])
        this.cloneRightList = this.cloneRightList.filter(item => item.id != id)
      }
    },


    // 右侧边输入框
    rightInputChange(ev) {
      console.log(ev.target.value)
      this.rightList = this.cloneRightList.filter(item => item.name.includes(ev.target.value))
    },

    // 左侧边输入框
    leftInputChange(ev) {
      console.log(ev.target.value)
      this.leftList = this.cloneLeftList.filter(item => item.name.includes(ev.target.value))
    },

  }
}


</script>
<style>
* {
  margin: 0;
  padding: 0;
}

#box {
  display: flex;
  justify-content: center;
  align-items: center;
}

.box {
  border: 1px solid rgb(235, 238, 245);
}

.shuttleFrame .el-dialog__footer {
  padding: 0px 20px 20px 0px !important;
}

.box .title {
  font-size: 14px;
  box-sizing: border-box;
  padding-right: 10px;
  display: flex;
  justify-content: space-between;
  width: 245px;
  height: 40px;
  line-height: 40px;
  background: #f5f7fa;
}

.box .title span:first-child {
  display: inline-block;
  background-size: 20px 20px;
  padding-left: 35px;
  cursor: pointer;
}

.box .title span:first-child.active {
}

.left, .right {
  width: 245px;
  height: 250px;
  padding: 5px 0;
  box-sizing: border-box;
  overflow-y: scroll;
  overflow-x: hidden;
}

.left > div, .right > div {
  cursor: pointer;
  text-align: left;
  background-repeat: no-repeat;
  background-position: 10px center;
  background-size: 20px 20px;
  font-size: 14px;
  padding-left: 35px;
  line-height: 30px;
}

.left > div.active, .right > div.active {
  background: #409eff;
  color: #ffffff;
}

.left .hover-color.active:hover, .right .hover-color.active:hover {
  color: #ffffff;
}

.left > div.hover-color:hover, .right > div.hover-color:hover {
  color: rgb(64, 158, 255);
}

.middle {
  margin: 0 20px;
}

.middle > div {
  margin: 10px 0;
  display: flex;
  justify-content: center;
  align-items: center;
  cursor: pointer;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid #dcdfe6;
  font-size: 12px;
  background-color: #f5f7fa;
  color: #c0c4cc;
}

.middle > div.active {
  background-color: #409eff;
  border-color: #409eff;
  color: #ffffff;
}

.search_parent {
  display: flex;
  justify-content: center;
  align-items: center;
  margin: 20px 0;
}

.search_input {
  width: 220px;
  height: 40px;
  border-radius: 50px;
  border: 1px solid #ccc;
  outline: none;
  font-size: 14px;
  text-indent: 20px
}



::-webkit-input-placeholder {
  color: rgb(192, 196, 204);
}

:-moz-placeholder {
  color: rgb(192, 196, 204);
}

::-moz-placeholder {
  color: rgb(192, 196, 204);
}

:-ms-input-placeholder {
  color: rgb(192, 196, 204);
}
</style>

THE END

发表回复