首页 > 解决方案 > jQuery Draggable - 在 html5 原生 Drag And Drop api 事件上创建可拖动并开始拖动

问题描述

好的,基本上我想要实现的是,当一个dragover事件从.HTML5 Drag And Drop APIdraggabledragendHTML5 Drag And Drop API

我想这样做的原因如下:

我对这个问题提出的解决方案是,使用 native HTML5 Drag and Drop API,当拖动元素到达 a 时,在浏览器窗口 Bdropzone中创建一个新的可拖动元素,并在其上模拟一个事件,因此它开始跟随光标。当事件触发时,我计划简单地在元素上触发事件,从这里开始,调度程序插件可以做到这一点。mousedowndragendmouseupdraggable

为了尝试对此进行测试,首先使用单个浏览器窗口,我尝试实现上述解决方案的第一部分,即:当dragover触发时,创建jQuery.ui draggable并模拟mousedown它,然后它应该开始跟随鼠标. 我无法实现这种行为。

我做了一个小提琴,在那里你可以看到我到目前为止尝试过的东西(我没有在这里发布整个代码,因为它相当长):JSFiddle

基本上,我在 Fiddle 上遇到的错误,我尝试了这两个选项,都是一个type.indexOf is not a function错误。

我还就以下问题询问并获得了一些帮助,建议的解决方案在使用事件启动拖动操作时click可以正常工作,但它不适用于任何其他事件类型。mousedown.draggable我强调,我只能从 a模拟一个事件,MouseEvent而该dragend事件不是 a MouseEvent

长话短说,我需要帮助才能获得我正在寻找的结果,至少对于我提出的解决方案的第一部分!

标签: javascriptjquery-uihtml5-draggable

解决方案


对此似乎没有一个好的答案。首先,并非所有浏览器都支持相同的 DnD 术语或功能。例如,FireFox 会在放置时触发事件,而当对象来自另一个窗口时dragenter,Chrome 似乎不会检测到事件。drop

这是我到目前为止的测试。要使用,请将内容复制到文本文件中并另存为 HTM 或 HTML。然后在浏览器中本地打开文件。打开另一个窗口并打开第二个 HTM。现在你有两个可以拖拽的窗口。

wina-1.htm

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Window A</title>
  <link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
  <style>
  .items {
    position: relative;
  }
  .items > div {
    margin-right: 5px;
    width: 150px;
    height: 150px;
    padding: 0.5em;
    border-radius: 6px;
    display: inline-block;
  }
  #log {
    width: 100%;
    height: 5em;
    overflow-y: auto;
  }
  [draggable].idle {
    background-color: rgba(255,0,0,0.75); 
  }
  [draggable].selected {
    background-color: rgba(255,0,0,0.95);
  }
  </style>
</head>
<body>
<pre id="log"></pre>
<div class="items ui-widget">
  <div id="draggable" class="ui-widget-content idle" draggable="true">
    <p>Drag me around</p>
  </div>
  <div id="static" class="ui-widget-content">
    <p>I can't be moved</p>
  </div>
</div>
<script>

var srcEl;

function log(s){
  var now = new Date();
  var t = now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds() 

+ "." + now.getMilliseconds();
  var l = document.getElementById("log");
  l.append(t + ": " + s + "\r\n");
  l.scrollTop = l.scrollHeight;
}

function dragStart(e){
  log("Drag Start: " + e.target.nodeName + "#" + e.target.id);
  srcEl = e.target;
  if(e.dataTransfer == undefined){} else {
    e.dataTransfer.effectAllowed = "copyMove";
    log("Event dataTransfer.effectAllowed: " + 

e.dataTransfer.effectAllowed);
    log("Source Element: " + srcEl.nodeName + "#" + srcEl.id);
  }
  this.classList.add("selected");
}

function dragOver(e){
  e.preventDefault();
  log("Drag Over: " + e.target.nodeName + (e.target.id != "" ? "#" + 

e.target.id : ""));
  return false;
}

function dragLeave(e){
  log("Drag Leave: " + e.target.nodeName + (e.target.id != "" ? "#" + 

e.target.id : ""));
}

function dragStop(e){
  log("Drag End: " + e.target.nodeName + "#" + e.target.id);
  this.classList.remove("selected");
}

log("Init");

var item = document.getElementById("draggable");
item.addEventListener('dragstart', dragStart, false);
item.addEventListener('dragover', dragOver, false);
item.addEventListener('dragleave', dragLeave, false);
window.addEventListener('dragleave', dragLeave, false);
var items = document.querySelectorAll('.items > div');
[].forEach.call(items, function(el) {
  el.addEventListener('dragover', dragOver, false);
});
</script>
</body>
</html>

如您所见,这是使用原始 JavaScript。我正在修补 jQuery UI,我保留样式表只是为了方便主题化。我们有一个部分用于打印日志详细信息、一个可拖动项和一个静态项。

winb-1.htm

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Window B</title>
  <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
  <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
  <style>
.drag-item {
  width: 100px;
  height: 100px;
  background-color: red;
}

body {
  position: relative;
}

div.drag-helper {
  width: 100px;
  height: 100px;
  background-color: red;
  z-index: 1002;
  position: relative;
}

#log {
  width: 100%;
  height: 5em;
  line-height: 1em;
  font-size: 1em;
  overflow-y: auto;
}

#dropzone {
  background-color: green;
  width: 95%;
  height: 340px;
}
  </style>
</head>
<body>
  <pre id="log"></pre>
  <div id="dropzone"></div>
<script>
jQuery(function($) {
  function log(s){
    var now = new Date();
    var t = now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds

() + "." + now.getMilliseconds();
    $("#log").append(t + ": " + s + "\r\n").scrollTop($("#log").prop

("scrollHeight"));
  }

  function dragEnter(e){
    e.preventDefault();
    log("Drag Enter triggered: " + $(e.target).prop("nodeName") + 

($(e.target).attr("id").length ? "#" + $(e.target).attr("id") : ""));
  }

  function dragOver(e){
    log("Drag Over triggered: " + $(e.target).prop("nodeName") + 

($(e.target).attr("id").length ? "#" + $(e.target).attr("id") : ""));
    e.dataTransfer.dropEffect = 'move';
    e.preventDefault();
  }

  function handleDrop(e){
    if (e.stopPropagation) {
      e.stopPropagation();
    }
    log("Drop Triggered: " + $(e.target).attr("id"));
    return false;
  }

  function dragEnd(e){
    log("Drag End Triggered: " + $(e.target).prop("nodeName") + 

($(e.target).attr("id").length ? "#" + $(e.target).attr("id") : ""));
  }

  log("Init");

  $("#dropzone").on({
    dragenter: dragEnter,
    dragover: dragOver,
    drop: handleDrop,
    mouseup: handleDrop,
    dragend: dragEnd
  });

  $(window).on({
    dragenter: dragEnter,
    dragover: dragOver,
    drop: handleDrop,
    dragend: dragEnd
  });

});
</script>
</body>
</html>

窗口 B 使用 jQuery,因为其目的是将元素转换为 jQuery UI Draggable。

首先要知道,在运输过程中没有办法。由于 Source 元素不是目标 DOM 的一部分,因此无法完成。它可以在drop事件中添加并初始化为 Draggable。基本上会发生的事情是在分配所有数据的时候创建一个新元素。

其次,数据传输不可靠,我会避免将 DataTransfer 作为您的数据容器。我建议使用 localStorage。这类似于 cookie,并且更可靠。

例如,我创建了以下 Data 对象:

{
  id,
  type,
  attr: {
    id,
    class,
    width,
    height
  },
  content
}

以下是一些示例函数:

function collectData(obj){
  return {
    id: obj.attr("id"),
    type: obj.prop("nodeName"),
    attr: {
      id: obj.attr("id"),
      class: obj.attr("class"),
      width: obj.width(),
      height: obj.height()
    },
    content: obj.text().trim()
  };
}

function saveData(k, d){
  localStorage.setItem(k, JSON.stringify(d));
}

function getData(k){
  return JSON.parse(localStorage.getItem(k));
}

function makeEl(d, pObj){
  return $("<" + d.type +">", d.attr).html("<p>" + d.content + "</p>").appendTo(pObj);
}

$("#draggable").on('dragstart', function(e){
  saveData("drag-data", collectData($(this)));
});

$("#dropzone").on('drop', function(e){
  var item = makeEl(getData('drag-data'), $(this));
  item.addClass("clone").position({
    my: "center",
    of: e
  }).draggable();
});

从理论上讲,这一切都应该奏效。在实践中,我遇到了很多障碍。我会建议类似点击复制类型的操作。用户单击窗口 A 中的项目(选择它),然后单击他们希望它在窗口 B 中的位置。再次使用 localStorage,可以将项目克隆到新位置。

wina-3.htm

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>jQuery UI Draggable - Default functionality</title>
  <link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
  <style>
  .items {
    position: relative;
  }
  .items > div {
    margin-right: 5px;
    width: 150px;
    height: 150px;
    padding: 0.5em;
    border-radius: 6px;
    display: inline-block;
  }
  #log {
    width: 100%;
    height: 5em;
    overflow-y: auto;
  }
  [draggable].idle {
    background-color: rgba(255,0,0,0.5); 
  }
  [draggable].selected {
    background-color: rgba(255,0,0,0.95);
  }
  </style>
</head>
<body>
<pre id="log"></pre>
<div class="items ui-widget">
  <div id="draggable" class="ui-widget-content idle" draggable="true">
    <p>Click on me</p>
  </div>
  <div id="static" class="ui-widget-content">
    <p>I can't be moved</p>
  </div>
</div>
<script>
var intv;

function log(s){
  var now = new Date();
  var t = now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds() + "." + now.getMilliseconds();
  var l = document.getElementById("log");
  l.append(t + ": " + s + "\r\n");
  l.scrollTop = l.scrollHeight;
}

function collectData(el){
  return {
    id: el.id,
    type: el.nodeName,
    attr: {
      id: el.id,
      class: el.className,
      width: el.width,
      height: el.height
    },
    content: el.innerText
  };
}

function saveData(k, v){
  localStorage.setItem(k, JSON.stringify(v));
}

function getData(k){
  return JSON.parse(localStorage.getItem(k));
}

function clearData(k){
  localStorage.setItem(k, null);
}

function selElem(e){
  var trg = e.target.nodeName + (e.target.id != "" ? "#" + e.target.id : "");
  if(e.target.classList.contains("selected")){
    log("Deselect element: " + trg);
    e.target.classList.remove("selected");
  } else {
    log("Element Selected: " + trg);
    e.target.classList.add("selected");
    saveData("select-data", collectData(e.target));
  }
  intv = setInterval(function(){
    if(getData("select-data") == null){
      document.getElementsByClassName("selected")[0].classList.remove("selected");
      log("Unselected");
      clearInterval(intv);
    }
  }, 1000);
}

log("Init");

var item = document.getElementById("draggable");
item.addEventListener('click', selElem);
</script>
</body>
</html>

winb-3.htm

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Window B</title>
  <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
  <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
  <style>
.drag-item {
  width: 100px;
  height: 100px;
  background-color: red;
}

body {
  position: relative;
}

#log {
  width: 100%;
  height: 5em;
  line-height: 1em;
  font-size: 1em;
  overflow-y: auto;
}

#dropzone {
  background-color: green;
  width: 95%;
  height: 340px;
  position: relative;
}

.cloned {
  position: absolute;
  width: 150px;
  height: 150px;
  padding: 0.5em;
  border-radius: 6px;
  display: inline-block;
  background-color: rgba(255,0,0,0.75);
}
  </style>
</head>
<body>
  <pre id="log"></pre>
  <div id="dropzone"></div>
<script>
jQuery(function($) {
  function log(s){
    var now = new Date();
    var t = now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds

() + "." + now.getMilliseconds();
    $("#log").append(t + ": " + s + "\r\n").scrollTop($("#log").prop

("scrollHeight"));
  }

  function getData(k){
    console.log("Getting Data: '" + k + "'", localStorage.getItem(k));
    return JSON.parse(localStorage.getItem(k));
  }

  function clearData(k){
    log("Clear Data");
    localStorage.setItem(k, null);
  }

  function makeEl(dObj, pObj){
    console.log(dObj, pObj);
    return $("<" + dObj.type + ">", dObj.attr).html("<p>" + dObj.content + 

"</p>").appendTo(pObj);
  }

  function handleDrop(e){
    if (e.stopPropagation) {
      e.stopPropagation();
    }
    var trg = $(e.target);
    log("Drop Triggered: " + trg.prop("nodeName") + "#" + trg.attr("id"));
    var d, item;
    if(e.target.id == "dropzone" && (e.type == "click" || e.type == 

"mouseup")){
      log("Click Detected - Collecting Data");
      d = getData("select-data");
      console.log("Data", d);
      d.attr.id = "clone-" + ($("#dropzone .cloned").length + 1);
      log("Making Element: " + d.type + "#" + d.attr.id);
      item = makeEl(d, trg);
      item.removeClass("selected").addClass("cloned").position({
        my: "center",
        of: e
      }).draggable();
      clearData("select-data");
      return true;
    }
    return false;
  }

  log("Init");

  $("#dropzone").on({
    mouseup: handleDrop,
    click: handleDrop
  });
});
</script>
</body>
</html>

我知道这不是您要寻找的答案,为此您需要尝试提出真正的问题。你似乎一直在问这个问题。

希望这可以帮助。


推荐阅读