首页 > 解决方案 > jQuery UI 可拖动未绑定到父边界

问题描述

我创建了一个基于 js 的节点编辑器。我有一个 div 元素用作节点的父级。尽管我将包含定义为父级,但您可以将节点移动到父级边界之外。但是,您不能将其移动到低于最后一个文档元素的位置。 在此处输入图像描述

重要的html代码是:

    <button type="button" class="btn btn-primary" onclick="sendData(fetchData())">Send data</button>
    <div id="node-container" class="node-container" style="height: 500px; width: 90%">
        <svg id="svg"/>
    </div>
    <button type="button" class="btn btn-primary" onclick="sendData(fetchData())">Send data</button>

可拖动的js是:

$(this.domElement).draggable({
    containment: 'parent',
    cancel: '.connection,.output,.data',
    drag: function(event, ui){
        that.updatePosition();
    }
});

也许我在育儿方面做错了,所以这里也是:

this.domElement = document.createElement('div');
document.getElementById('node-container').appendChild(this.domElement);

我没有过多地使用 JavaScript,并且仍在学习!

编辑:这是节点容器的定义:

<div id="node-container" class="node-container" style="height: 500px; width: 90%">
</div>

完成 StoryNode.js: https ://hastebin.com/kiyoyekoce.js

// ===================================================================
// Node class. Represents a javascript version of the java Node class
// ===================================================================

var nodes = []; //Have second reduced node version running, maybe as a method in the Node? Call simple for loop in web-link

function StoryNode(name) {
    this.name = name;

    let that = this;

    nodes.push(this);

    // Add our graphical representation

    this.domElement = document.createElement('div');
    document.getElementById('node-container').appendChild(this.domElement);

    this.domElement.classList.add('node');
    this.domElement.classList.add('container-fixed');
    this.domElement.setAttribute('title', name.replace("Node",""));

    let remDom = document.createElement('div');
    remDom.classList.add('delete-node');
    remDom.innerHTML ='<i class="fa fa-trash" aria-hidden="true"/>';
    this.domElement.appendChild(remDom);

    remDom.onclick = function (e) {

        that.detachAll();

        for( let i = 0; i < nodes.length; i++){
            if ( nodes[i] === that) {
                nodes.splice(i, 1);
            }
        }

        that.domElement.parentElement.removeChild(that.domElement);
    };

    // Add the node body

    // Directories
    var innerDomGroup = document.createElement('div');
    innerDomGroup.classList.add('row');
    this.domElement.appendChild(innerDomGroup);

    // Outputs
    this.outputDomGroup = document.createElement('div');
    this.outputDomGroup.classList.add('col-fixed-small-1');
    innerDomGroup.appendChild(this.outputDomGroup);

    // Inner Data
    this.dataDomGroup = document.createElement('div');
    this.dataDomGroup.classList.add('col-fixed-medium-1');
    innerDomGroup.appendChild(this.dataDomGroup);

    // Inputs
    this.inputDomGroup = document.createElement('div');
    this.inputDomGroup.classList.add('col-fixed-small-1');
    innerDomGroup.appendChild(this.inputDomGroup);


    //Node data
    this.inputs = [];
    this.data = [];
    this.outputs = [];
}

StoryNode.prototype.addInput = function(name, inputType){
    let input = new NodeInput(name, inputType);
    input.node = this;
    this.inputs.push(input);
    this.inputDomGroup.appendChild(input.domElement);

    return input;
};

StoryNode.prototype.addOutput = function(name, outputType){
    let output = new NodeOutput(name, outputType);
    output.node = this;
    this.outputs.push(output);
    this.outputDomGroup.appendChild(output.domElement);

    return output;
};

StoryNode.prototype.addData = function(name, outputType){
    let data = new NodeData(name, outputType);
    data.node = this;
    this.data.push(data);
    this.dataDomGroup.appendChild(data.domElement);

    return data;
};

StoryNode.prototype.ownsInput = function(input){
    for(let i = 0; i < this.inputs.length; i++){
        if(this.inputs[i] === input)
            return true;
    }
    return false;
};

Node.prototype.ownsOutput = function(output){
    for(let i = 0; i < this.outputs.length; i++){
        if(this.outputs[i] === output)
            return true;
    }
    return false;
};

StoryNode.prototype.detachAll = function(output){
    for(let item of this.inputs){
        if(item.output)
            item.output.detachInput();
    }

    for(let item of this.outputs){
        if(item.input)
            item.detachInput();
    }
};

StoryNode.prototype.updatePosition = function(){

    for(let j = 0; j < this.outputs.length; j++){
        if(this.outputs[j].input != null){
            let oP = this.outputs[j].getOutputPoint();
            let iP = this.outputs[j].input.getAttachPoint();

            let pStr = this.createPath(iP, oP);
            this.outputs[j].input.path.setAttributeNS(null, 'd', pStr);
        }
    }

    for(let j = 0; j < this.inputs.length; j++){
        if(this.inputs[j].output != null){
            let iP = this.inputs[j].getAttachPoint();
            let oP = this.inputs[j].output.getOutputPoint();

            let pStr = this.createPath(iP, oP);
            this.inputs[j].path.setAttributeNS(null, 'd', pStr);
        }
    }
};

StoryNode.prototype.createPath = function(a, b){
    let diff = {
        x: b.x - a.x,
        y: b.y - a.y
    };

    let pathStr = 'M' + a.x + ',' + a.y + ' ';
    pathStr += 'C';
    pathStr += a.x + diff.x / 3 * 2 + ',' + a.y + ' ';
    pathStr += a.x + diff.x / 3 + ',' + b.y + ' ';
    pathStr += b.x + ',' + b.y;

    return pathStr;
};

StoryNode.prototype.moveTo = function(point){
    this.domElement.style.top = point.y + 'px';
    this.domElement.style.left = point.x + 'px';
    this.updatePosition();
};

StoryNode.prototype.initUI = function(){
    let that = this;
    // Make draggable
    // noinspection JSValidateTypes
    $(this.domElement).draggable({
        containment: 'parent',
        cancel: '.connection,.output,.data',
        drag: function(event, ui){
            that.updatePosition();
        }
    });

    //Mak node design smaller as it holds no data inputs
    if(this.data.length === 0) {
        this.domElement.classList.remove('container-fixed');
        this.domElement.classList.add('container-fixed-min');

        this.dataDomGroup.classList.remove('col-fixed-medium-1');
        this.dataDomGroup.classList.add('col-fixed-medium-1-min');
    }

    // Fix positioning
    this.domElement.style.position = 'absolute';

    document.body.appendChild(this.domElement);
    // Update Visual
    this.updatePosition();
};

// Node 1
let node = new StoryNode('Page');


// Move to initial positions
node.moveTo({x: 150, y: 20});

// Connect Nodes


// Add to DOM
node.initUI();
body{
    background-color: #101010;
    color: #d4d4d4;
    font-family: sans-serif;
}
.node:before{
    content:attr(title) " ";
    display: block;
    border-top-left-radius:.75em;
    border-top-right-radius:.75em;
    background-color:#6e6e6e;
    padding:0.1em .3em 0em;
    margin:-.1em -.3em 0.2em;
}
.node{
    background-color: #4e4e4e;
    border-radius: .75em;
    display: inline-block;
    padding:0.1em .3em .25em;
    position:absolute;
    cursor: move;
}
.connection:after{
    position:absolute;
    border:solid 1px #dedede;
    background-color: #2e2e2e;
    width:0.5em;
    height:0.5em;
    border-radius:0.5em;

}

.delete-node{
    display: inline-block;
    font-weight: 400;
    text-align: center;
    white-space: nowrap;
    vertical-align: middle;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    font-size: 1rem;
    line-height: 1.5;
    transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;

    position:absolute;
    color: #b8b8b8;
    background-color: transparent;
    border-radius:0.5em;
    width:22px;
    height:22px;

    cursor: default;

    top: 3px;
    right: 8px;
}

.connection.filled:after{
    border:solid 1px transparent;
}
.connection:hover:after{
    border-color:red;
}
.output{
    left: .1em;
    top:.1em;
    cursor: pointer;
}
.input{
    left: 2em;
    top: .1em;
    cursor: pointer;
}
.connection{
    width:100%;
    position:relative;
    padding-right:0.5em;
    cursor:pointer;
}
.connection:after{
    content:"";
    right:0em;
    top:0.25em;
}

.filled-node:after{
    background-color: #d4d9de;
}
.empty-node:after{
    border: 2px solid #d4d9de;
}

.filled-boolean:after{
    background-color: #4f8fde;
}
.empty-boolean:after{
    border: 2px solid #4f8fde;
}


svg{
    position:absolute;
    top:0px;
    left:0px;
    z-index:-100;
    width:100%;
    height:100%;
}

textarea {
    resize: none;
}

/* The switch - the box around the slider */
.switch {
    position: relative;
    display: inline-block;
    width: 60px;
    height: 34px;
}

/* Hide default HTML checkbox */
.switch input {
    opacity: 0;
    width: 0;
    height: 0;
}

/* The slider */
.slider {
    position: absolute;
    cursor: pointer;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: #ccc;
    -webkit-transition: .4s;
    transition: .4s;
}

.slider:before {
    position: absolute;
    content: "";
    height: 26px;
    width: 26px;
    left: 4px;
    bottom: 4px;
    background-color: white;
    -webkit-transition: .4s;
    transition: .4s;
}

input:checked + .slider {
    background-color: #2196F3;
}

input:focus + .slider {
    box-shadow: 0 0 1px #2196F3;
}

input:checked + .slider:before {
    -webkit-transform: translateX(26px);
    -ms-transform: translateX(26px);
    transform: translateX(26px);
}

/* Rounded sliders */
.slider.round {
    border-radius: 34px;
}

.slider.round:before {
    border-radius: 50%;
}

.round-edge {
    border-radius: 15px;
}
.container-fixed {
    width: 200px !important;
}

.container-fixed-min {
    width: 100px !important;
}

.col-fixed-small-1 {
    width: 5px;
}

.col-fixed-small-2 {
    width: 5px;
}
.col-fixed-medium-1 {
    width: 180px;
    padding-left: 15px;
    margin-right: -12px;
}

.col-fixed-medium-1-min {
    width: 80px;
    padding-left: 15px;
    margin-right: -12px;
}

.node-container {
    background-color: #2e2e2e;
    border-radius: 10px;
    height: 500px;
    width: 90%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
        <script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js"></script>

<div id="node-container" class="node-container">
    <svg id="svg"/>
</div>
        <button type="button" class="btn btn-primary">Send data</button>

标签: javascriptjquery-ui

解决方案


我怀疑问题出在您附加元素时。如果初始化draggable的时候没有追加,可能是看不懂parent关系。

考虑以下示例:

$(function() {
  function addNode(title, content, tObj) {
    if (title == undefined) {
      title = "New Node";
    }
    if (content == undefined) {
      content = "<p></p>";
    }
    if (tObj == undefined) {
      tObj = $("#node-container");
    }
    var node = $("<div>", {
      class: "node-element"
    }).appendTo(tObj);

    $("<div>", {
      class: "node-title"
    }).html(title).appendTo(node);

    $("<span>", {
      class: "btn btn-delete-node"
    }).html("x").click(function() {
      var r = confirm("Are you sure you want to delete this node?");
      if (r) {
        $(this).closest(".node-element").remove();
      }
    }).appendTo($(".node-title", node));

    $("<div>", {
      class: "node-content"
    }).html(content).appendTo(node);

    node.draggable({
      containment: 'parent',
      handle: ".node-title",
      cancel: '.connection,.output,.data',
      stop: function(e, ui) {
        // update position
      }
    });
  }
  $(".add-node-btn").click(function() {
    addNode();
  });
});
body {
  background-color: #000;
  font-family: "arial", san-seriff;
}

.node-container {
  background-color: #2a2a2a;
  border-radius: 10px;
  margin: 3px;
}

.btn-primary {
  border: 0;
  border-radius: 6px;
  padding: 4px;
  background-color: #0000ff;
  color: #FFF;
}

.node-container .node-element {
  background-color: #212121;
  border-radius: 6px;
  border: 0;
  width: 125px;
  height: 75px;
}

.node-container .node-element .node-title {
  background-color: #484848;
  border-radius: 6px 6px 0 0;
  border: 0;
  min-height: 1em;
  color: #fff;
  text-align: center;
}

.node-container .node-element .node-title .btn-delete-node {
  float: right;
  padding-right: 6px;
  cursor: pointer;
}
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<button type="button" class="btn btn-primary">Send data</button> <button type="button" class="btn btn-primary add-node-btn">Add Node</button>
<div id="node-container" class="node-container" style="height: 500px; width: 90%">
  <svg id="svg" />
</div>
<button type="button" class="btn btn-primary">Send data</button>

由于您的示例代码不是 Minimal、Complete 或 Verifiable 示例,因此很难确定添加元素与初始化元素的时间或方式。

希望有帮助。


推荐阅读