我已经创建了一个基于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>
答案 0 :(得分:1)
我怀疑问题出在添加元素时。如果在初始化可拖动对象时未附加它,则可能无法理解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>
由于示例代码不是最小,完整或可验证的示例,因此很难确定何时以及如何添加元素以及如何初始化元素。
希望有帮助。