简体   繁体   中英

jQuery Draggable - Create draggable and start dragging on html5 native Drag And Drop api event

Okay, basically what I want to try to achieve, is when a dragover event fires from HTML5 Drag And Drop API , I wish to create a jQuery draggable object, and start following the mouse around, while the dragend event fires from the HTML5 Drag And Drop API .

The reason I want to do this, is the following:

  • I have an application which uses a plugin, that has a functionality, which is dependent on the jQuery.ui draggable to function (it is the FullCalendar Scheduler plugin, version 3)
  • I want to achieve a new functionality in the application, with which the client can drag something from browser window A , and drop it in the above mentioned plugin in browser window B .
  • As the above mentioned plugin is not working with the native HTML5 Drag and Drop API , and the jQuery.ui draggable is not capable of dragging elements from one browser window to the other, I think my only option is to mix these two plugins.

My proposed solution to this problem was, using the native HTML5 Drag and Drop API , and when the dragged element reaches over a dropzone , creating a new draggable element in browser window B , and simulating a mousedown event on it, so it starts following the cursor. When the dragend event would fire, I planned to plain and simply fire the mouseup event on the draggable element also, and from here on the scheduler plugin can do it's magic.

To try to test this out, with a single browser window at first, I've tried to achieve the first part of my above solution, ie: when the dragover fires, create the jQuery.ui draggable and simulate a mousedown on it, then it should start following the mouse. I can't achieve this behaviour.

I made a fiddle, where you can see what I tried so far (I am not posting the whole code here, as it is rather long): JSFiddle

Basically, the error I am getting at the Fiddle, with both options that I tried, is a type.indexOf is not a function error.

I also asked and received some help on the following question , from where the proposed solution works fine when starting the drag operation with a click event, but it isn't working with any other event type. I pressume, I can simulate a mousedown.draggable event, only from a MouseEvent , and the dragend event is not a MouseEvent .

Long story short, I would need help in obtaining the result I am looking for, at least for the first part of my proposed solution!

There does not appear to be a good answer for this. First, not all browsers support the same DnD terminology or functionality. Such as FireFox fires a dragenter event on drop and Chrome does not seem to detect a drop event when the object is from another window.

Here is my testing so far. To use, copy the content into a Text file and save as HTM or HTML. Then Open the file locally in your browser. Open another Window and open the second HTM. now you have two windows you can drag to and from.

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>

As you can see, this is using raw JavaScript. I was tinkering with jQuery UI, and I kept the stylesheet just for easy theming. We have a section to print out log details, a draggable, and a static item.

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>

Window B uses jQuery as the intention was to convert the element into a jQuery UI Draggable.

First thing to know, there is no way to do in transit. Since the Source element is not a part of the target DOM, it cannot be done. It can be added and initialized as a Draggable in the drop event. Essentially what will happen is a new element will be created at that time assigned all the data.

Second, data transfer is unreliable and I would avoid DataTransfer as your data container. I would advise using localStorage. This is similar to a cookie and is a lot more reliable.

For example, I created the following Data object:

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

Here are some example functions:

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();
});

In theory, this should all work. In practice, I have hit a ton of roadblocks. I would suggest something like a click-to-copy type of action. Where the User clicks an item in Window A (selecting it) and then clicks where they want it to be in Window B. Again using localStorage, the item could be cloned into the new location.

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>

I know this is not the answer you're looking for, and for that you need to try to ask the real question. You seem to keep asking around the question.

Hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM