I want to be able to select cloneable draggable objects (red circles) by left-clicking and then delete them by clicking on the button Delete selected circle. I want to enable deleting only those objects that are located INSIDE .dropzone.
Here is my JSFIDDLE.
The .dropzone is a grey-colored div, and a draggable object is a red circle.
This is the code for deleting an object:
function removeObject(div) {
div.querySelector(".draggable").parentNode.removeChild(elem);
return false;
}
I pass the class of a cloned object, however, it gets undetected and therefore I cannot delete it. Also I don't know how to limit the deleting operation to only those objects that are inside .dropzone.
Check this out, add a new class to each cloned object so you can tell the difference and pass a class that will be specific to that circle to the remove function. Using this, you could also add a class to the object when dropped, so that way you know if it is in the box and remove when it is dropped outside of the box.
addClass append every time in every Clone
EDIT:
Here, this is the relevant changes I made to your fiddle, I only pasted the portions I changes. I commented out some of my code that was unnecessary, but I left it in because it might be of use to you. Feel free to delete though. Hope this is what you were looking for.
I made it so that the clones get a class of cloned and if they are dropped in the box area, they get a selected class added on. Then if another circle is touched, the selected class is removed and only gets added on if the last circle touched was moved or placed in the box, or in the box. If the circle is removed from the box, it loses the selected class.
.on('move', function(event) {
var interaction = event.interaction;
if (interaction.pointerIsDown && !interaction.interacting() && event.currentTarget.getAttribute('clonable') != 'false') {
var original = event.currentTarget;
var clone = event.currentTarget.cloneNode(true);
var x = clone.offsetLeft;
var y = clone.offsetTop;
/* var length = $(".cloned").length;
if(length == "0"){
clone.className = "draggable cloned cloned-0";
}
else{
clone.className = "draggable cloned cloned-"+length;
} */
clone.className = "draggable cloned";
clone.setAttribute('clonable', 'false');
clone.style.position = "absolute";
clone.style.left = original.offsetLeft + "px";
clone.style.top = original.offsetTop + "px";
original.parentElement.appendChild(clone);
interaction.start({
name: 'drag'
}, event.interactable, clone);
}
})
// enable draggables to be dropped into this
interact('.dropzone').dropzone({
// Require a 50% element overlap for a drop to be possible
overlap: 0.50,
// listen for drop related events:
ondropactivate: function(event) {
// add active dropzone feedback
event.target.classList.add('drop-active');
$('.cloned').removeClass('selected');
},
ondragenter: function(event) {
var draggableElement = event.relatedTarget,
dropzoneElement = event.target;
// feedback the possibility of a drop
dropzoneElement.classList.add('drop-target');
// draggableElement.classList.add('in-zone');
},
ondragleave: function(event) {
// remove the drop feedback style
// event.relatedTarget.classList.remove('in-zone');
event.target.classList.remove('drop-target');
},
ondrop: function(event) {
event.relatedTarget.classList.add('selected');
},
ondropdeactivate: function(event) {
// remove active dropzone feedback
event.target.classList.remove('drop-active');
event.target.classList.remove('drop-target');
}
});
Related
I have made this
https://jsfiddle.net/a4376mr8/
When I drag and drop the image div to a new div, why is it not there in the previous div?
const boxes = document.querySelectorAll('.box');
const imageBox = document.querySelector('#draggableItem');
for (const box of boxes) {
box.addEventListener('dragover', function (e) {
e.preventDefault();
this.className += ' onhover';
})
box.addEventListener('dragleave', function () {
this.className = 'box';
})
box.addEventListener('drop', function (e) {
this.className = 'box';
this.append(imageBox);
})
}
If I understood your needs, that the image be repeated on drag and drop, you need to clone your div tag dom object. Since js just sees the reference to it, when you simply append it, this causes it to just move from place to place instead of duplicating.
So instead of just appending, clone the node as follows (line 15 of your fiddle's js).
this.append(imageBox.cloneNode(true));
See here: https://developer.mozilla.org/en-US/docs/Web/API/Node/cloneNode
Updated fiddle: https://jsfiddle.net/a4376mr8/1/
So right now, I can dynamically create elements (2 rows of 12 blocks) and when I click on an individual block, I can change the color of it as well.
However, I am having one problem. When I click on a block to have its color changed, the color picker will pop up beside it, no issues at all. When I add a new set of rows and try to color the same block number, it will replace the color of the block from the previous row.
For example, if I color the 12th block in the first row, then add 2 new sets of rows and click on the same block in the second set, it will act as if I'm clicking on the previous set's block. I am using https://bgrins.github.io/spectrum/ as my colorPicker
Here is a link to what I have done so far:
http://codepen.io/anon/pen/bwBRmw
var id_num = 1;
var picker = null;
$(function () {
$(document).on('click', ".repeat", function (e) {
e.preventDefault();
var $self = $(this);
var $parent = $self.parent();
if($self.hasClass("add-bottom")){
$parent.after($parent.clone(true).attr("id", "repeatable" + id_num));
id_num = id_num + 1;
//picker = null;
} else {
$parent.before($parent.clone(true).attr("id", "repeatable" + id_num));
id_num = id_num + 1;
//picker = null;
}
});
});
$(".container").on("click", "a", function(e) {
var self = this;
console.log(this.id)
console.log(this)
$(self).spectrum({
color: "#f00",
change: function(color) {
$(self).css('background-color',color.toHexString());
}
});
e.stopPropagation();
})
The problem seems to be that you are cloning elements which already have the colorpicker events bound.
EDIT: I think I've managed to work around the problem by changing your use of jQuery's clone(). If you tell it to clone without including events (omitting the first parameter to clone() which defaults to false, the DOM objects will be created without the colorpicker pointing at the old ones.
Here's an example that I think is doing what you are looking for. I've just removed the true params for clone(). No changes to HTML or CSS.
I have created a functions that, when I click on a node, makes all non-neighboring nodes transparent*. Now I would like to make the same nodes unresponsive to mouse events, while keeping visible nodes responsive.
One option would be to assign the css attribute pointer-events:none to transparent nodes. Can I do this with sigma?
*To do so I assign an rgba color with opacity 0. Hence I must use the canvas renderer because WebGL does not support transparency.
My code:
function highlight () {
var s = sigma.instances()[0];
var nodes = s.graph.nodes();
var edges = s.graph.edges();
var maxCollab = d3.max(edges, function(d) { return d.collaborations; });
// We first need to save the original colors of our
// nodes and edges, like this:
nodes.forEach(function(n) {
n.originalColor = n.color;
});
edges.forEach(function(e) {
e.originalColor = e.color;
});
// When a node is clicked, we check for each node
// if it is a neighbor of the clicked one. If not,
// we set its color as grey, and else, it takes its
// original color.
// We do the same for the edges, and we only keep
// edges that have both extremities colored.
s.bind('clickNode', function(e) {
var nodeId = e.data.node.id,
toKeep = s.graph.neighbors(nodeId);
toKeep[nodeId] = e.data.node;
nodes.forEach(function(n) {
if (toKeep[n.id])
n.color = n.originalColor;
else
n.color = 'rgba(0,0,0,0)';
});
edges.forEach(function(e) {
if (toKeep[e.source] && toKeep[e.target]) {
e.color = e.originalColor;
}
else
e.color = 'rgba(0,0,0,0)';
});
// Since the data has been modified, we need to
// call the refresh method to make the colors
// update effective.
s.refresh();
});
// When the stage is clicked, we just color each
// node and edge with its original color.
s.bind('clickStage', function(e) {
nodes.forEach(function(n) {
n.color = n.originalColor;
});
edges.forEach(function(e) {
e.color = e.originalColor;
});
s.refresh();
});
}
Do you just want to hide the nodes? If so, you could set the hidden property of the nodes to true. This way they wouldn't be visible anymore and sigma wouldn't fire any events for them.
You can simply add a flag onto the node(s) meant to be unresponsive to the clickNode event.
// excerpt from `clickNode` handler
nodes.forEach(function(n) {
if (toKeep[n.id]) {
n.color = n.originalColor;
n.clickable = false; // <-- add this
} else {
n.color = 'rgba(0,0,0,0)';
}
});
Then only let the contents of the clickNode handler apply to those nodes.
// `clickNode` handler
s.bind('clickNode', function(e) {
if (e.data.node.clickable) {
// business as usual
}
});
Don't forget to set the flag to true in your clickStage handler.
I have an array of mapLayers coming in from a table in sql. These mapLayers have parent layers which contain one or more child layers. The element a that is being created is the one that holds the name of the layer
I would like to make the layer name to the parent layer visible by default and then only make the child layer names visible when the parent layer is clicked.
The kind of functionality I'm seeking is similar to that of the bootstrap accordion. My index emp[2] != null is what determines if the layer has a parent or not (if its a child).Iif it is a child I would like to hide it then only display when the parent is clicked, but since there is no ID on the "a" tag I am not sure how to set the onClick event. Also if I assing ID to a each time its created it will mean a will always have the same ID and that will cause problems.
$.when(getSecureData("/api/layers/"))
.then(function (retmaplayers) {
for (i = 0; i < retmaplayers.length; i++) {
addLayer(map, L.mapbox.tileLayer(retmaplayers[i][1]), retmaplayers[i][0], i + 1);
$.each(retmaplayers, function (index, emp) {
// pageViewModel.LocationViewModel.parentID(emp.fullname);
if (emp[2] != null)
{
pageViewModel.locationVM.parentID(1)
}
function addLayer(map, layer, name, zIndex) {
var layers = document.getElementById('menu-ui');
layer.setZIndex(zIndex)
layer.addTo(map);
// Create a simple layer switcher that
// toggles layers on and off.
var link = document.createElement('a');
link.href = '#';
if (zIndex == 1) {
link.className = 'active';
// link.class = 'accordion-toggle';
if(pageViewModel.locationVM.parentID()==1)
{
link.style.visibility = "hidden";
}
}
else {
map.removeLayer(layer);
this.className = '';
}
link.innerHTML = name;
if (zIndex != 1) {
link.onclick = function (e) {
e.preventDefault();
e.stopPropagation();
if (map.hasLayer(layer)) {
map.removeLayer(layer);
this.className = '';
} else {
layer.setOpacity(1.0);
map.addLayer(layer);
this.className = 'active';
}
};
}
layers.appendChild(link);
since there is no ID on the "a" tag I am not sure how to set the
onClick event
In general, you don't use elements' ids in Knockout (at least not in your viewmodel code), and in particular, you don't set onClick events. You use the click binding on the clickable element.
You do not seem to be embracing the spirit of Knockout, which is that you manipulate the view elements by doing data operations on your viewmodel. You're creating DOM elements in your viewmodel code instead of in a binding handler.
Your addLayer should just be inserting a row into an observableArray. You should have (probably) a foreach binding for that observableArray, and within it a custom binding handler that turns each element into a map layer.
I have a page with a large number of JQueryUI sliders, in div .origin. Each slider has an associated checkbox. If the checkbox is checked, the slider should be moved- with all values intact- to div .favourites. If the checkbox is unchecked, the slider should be moved back to .origin.
Here's a JSFiddle of my attempts so far.
Here's my function to detect the value of the checkbox and make DOM adjustments accordingly:
$('[type="checkbox"]').click(function () {
var check = $(this),
checked = $(check).prop("checked"),
num = $(check).attr("id").split("-")[1],
parent = $("#food-" + num),
parentContent = $(parent).clone(),
slider = "#" + $(parent).find('.slider').attr("id"),
favelist = $(".favourites .content");
alert(checked)
//remove the original
$(parent).remove();
if (checked === true) {
//add to favourites
$(favelist).append(parentContent);
} else {
//add to origin
$(favelist).append(parentContent);
}
//reinitialise slider
loadSlider(slider)
});
A number of things aren't working:
1) First and most significantly, after the slider and it's parent elements have been moved into the .origin div, clicks on the checkbox no longer register. The alert(checked) doesn't fire, and the else statement doesn't run. Why would a checkbox no longer register a click event after being moved in the DOM?
2) Secondly, the moved sliders have the correct value displayed on the highlight div, but the dragger-handles go back to the starting position. How can I re-initialise the slider with the dragger handle in the correct position?
--
UPDATE- JSFIDDLE
OK, first issue is resolved, thanks to the hint provided by Chad, below. I've rewritten the click event into two functions, so that the click event can be re-instantiated after the elements are re-created in the DOM:
//Listen for checkbox to be checked
function checkboxChecked() {
$('[type="checkbox"]').click(function () {
var checkbox = $(this);
moveItem(checkbox);
});
}
//Create/Destroy items when clicked
function moveItem(checkbox) {
var check = $(checkbox),
checked = $(check).prop("checked"),
num = $(check).attr("id").split("-")[1],
parent = $("#food-" + num),
parentContent = $(parent).clone(),
slider = "#" + $(parent).find('.slider').attr("id"),
favelist = $(".favourites .content"),
origin = $(".all .content");
$(parent).remove();
if (checked === true) {
$(favelist).append(parentContent);
} else {
$(origin).append(parentContent);
}
loadSlider(slider);
checkboxChecked();
}
//initialise checkbox move function
checkboxChecked();
Am still trying to solve the mystery of the handle positions being re-set to 0 despite the slider retaining it's value, as shown on the highlighter track.