I'm trying to create UI for advanced search so that the user can make search rule boxes and then drag and drop them on top of others to create a group of rule boxes. Each box has a drop event, and when one box is dropped on top of the other a new Div container is built and the two boxes are placed there. The container also has a separate drop event and allows a box to be placed in the container if it is dropped on . The problem is that when one box is in the container and the other box is placed on it, the container event occasionally is also triggered. How to prevent container event to be triggered ? Here it is some pictures of the UI :
The rule box drop event is :
$('.ruleBox').droppable({
tolerance: 'pointer',
accept: '.ruleBox,.subExpressionContainer',
drop: function (event, ui) {
let dropped = ui.draggable,
droppedOn = $(this),
droppedParent = dropped.parent();
let newId = generateID(1),
subExpressionContainer = MakeSubExpressionContainerVariable(newId[0]),//Makes the container's html
droppedOnParent = droppedOn.parent();
droppedOnParent.append(subExpressionContainer);//Append the container to the DOM
let droppedOnSubRuleContainer = $(`#subRuleBoxContainer-${newId[0]}`);
$(dropped).detach().css({ top: 0, left: 0 }).appendTo(droppedOnSubRuleContainer);
$(droppedOn).detach().css({ top: 0, left: 0 }).appendTo(droppedOnSubRuleContainer);
},
greedy: true
});
And here it is the container's droppable code :
function SetDropZoneEvent() {
$('.dropZone').droppable({
tolerance: 'pointer',
accept: '.ruleBox,.subExpressionContainer',
drop: function (event, ui) {
let dropped = ui.draggable,
droppedOn = $(this),
droppedParent = dropped.parent();
$(dropped).detach().css({ top: 0, left: 0 }).appendTo(droppedOn);
},
greedy: true
});
The code of the container maker :
function MakeSubExpressionContainerVariable(id) {
return `
<div class="subExpressionContainer" id="subExpressionContainer-${id}">
<button class="searchButtons" id="addEditButt" onclick="ToggleOperator(this)"
value="AND" >AND</button>
<div class="subRuleBoxFreeSpace">
</div>
<div class="subRuleBoxContainer dropZone" id="subRuleBoxContainer-${id}">
</div>
<button class="searchButtons" id="addEditButt"
onclick="RemoveSubExpressionContainer('subExpressionContainer-${id}')"
value="X" >X</button>
</div>`}
The code of rule box maker :
function MakeRuleBoxVariable(sPName, sPDName, sPType, sPRelation, sPDetailAction,
sCondition, sContent) {
let id = new Date().getTime();
return `
<div class="ruleBox" id="ruleBox-${id}" data-property-name = "${sPName}"
data-type = "${sPType}" data-relation = "${sPRelation}" data-detail-action =
"${sPDetailAction}" >
<div class="rule">
<div class="ruleText"><span style="height: 100%;">"${sPDName}"
"${sCondition}" "${sContent}"</span></div>
</div>
<div class="searchBoxButt">
<div class="searchBoxRemoveIcon"><span> </span><a
onclick="RemoveRuleBox('ruleBox-${id}')"><i class="fa fa-times">
</i></a><span> </span>
</div>
<div class="searchBoxEditIcon"><span> </span><i class="fa fa-edit"></i><span> </span>
</div>
</div>
</div>`
}
Related
HTML
<a class="level-item like-icon">
<div class="icon is-medium">
<i class="far fa-heart" onmouseover="change(true)" onmouseout="change(false)"></i>
</div>
</a>
JS
change = (state) => {
state
? event.currentTarget.setAttribute('data-prefix', 'fas')
: event.currentTarget.setAttribute('data-prefix', 'far');
};
Goal:
To change icon (class, or in this case, attribute) when someone hovers over the icon and revert it back when the user hovers out of it. The above code seems to work but there are a couple issues. 1) It fires way to many times when I hover over it and 2) Many times, it doesn't change the attribute back to "far" (state = false). I tried attaching those events to <a> instead of <li> but the issues persist.
p.s. NOT using JQUERY
Something like this one ?
Here i adding and removing a class hover, but ti also may be any attribute or something else
window.addEventListener('mousemove', e => {
let hovered = document.querySelector('.hover');
if (e.target === hovered) return;
if (hovered) {
console.log('mouse out from', hovered);
hovered.classList.remove('hover');
}
if (!e.target.classList.contains('icon'))
return;
e.target.classList.add('hover');
console.log('mouse over on', e.target)
})
.icon {
display: inline-block;
width: 50px;
height: 50px;
transition: 100ms;
border: solid;
text-align: center;
line-height: 50px;
}
.hover {
color: red;
border-radius: 30%;
transform: rotate(10deg)
}
<div class="icon">1</div>
<div class="icon">2</div>
<div class="icon">3</div>
<div class="icon">4</div>
<div class="icon">5</div>
<div class="icon">6</div>
<div class="icon">7</div>
There are 7 'onmouse...' events...
onmousedown,
onmouseenter,
onmouseleave,
onmousemove,
onmouseout,
onmouseover,
onmouseup
... so it is important to use the right one for the job.
In the example clicking and mouse movement within the Element doesn't apply - all we want is a function to be called once when the mouse enters and the element and once agian when the mouse leaves. Therefore...
<!-- HTML -->
<a class="level-item like-icon">
<div class="icon is-medium">
<i class="far fa-heart"
onmouseenter="change(this)"
onmouseleave="change(this)"></i>
</div>
</a>
So here it seems sensible to use the onmouseenter and onmouseleave attributes to call the change() function, and in this case to passes the HTML Element under the mouse as an argument via the this keywords.
Now the function can scrutinize the element and check if it has the desired and required 'data-prefix' attribute, and if so what that attribute is set to. We can then use this condition to set/reset the 'data-prefix' attribute's value...
/* JavaScript */
change = (elem) => {
// does the Element have a 'data-prefix' attribute
// and what is it set to?
let isSet = elem.hasAttribute("data-prefix") && (
"far" === elem.getAttribute("data-prefix")
);
elem.setAttribute("data-prefix", (isSet ? "fas" : "far"));
}
However, as has already been mentioned using the Element.addEventListener() method is more robust and flexible than relying on HTML attributes like onmouse....
This sounds like a duplicate of How do I simulate a mouseover in pure JavaScript that activates the CSS ":hover"?
It's not ideal to deal with mouseover in pure JS, but here is a working example (insipired by an answer to the post I linked).
var element = document.getElementById('hoverIcon');
element.addEventListener('mouseover', function() {
console.log('Mouse over, set Font Awesome class here');
});
element.addEventListener('mouseout', function() {
console.log('Mouse out, remove Font Awesome class here');
});
var event = new MouseEvent('mouseover', {
'view': window,
'bubbles': true,
'cancelable': true
});
<a class="level-item like-icon">
<div class="icon is-medium">
<i id="hoverIcon" class="far fa-heart">ICON</i>
</div>
</a>
Here is a second version of my initial answer, this time with multiple elements.
var elements = document.getElementsByClassName("hover-icon");
var i;
for (i = 0; i < elements.length; i++) {
element = elements[i];
element.addEventListener('mouseover', function(data) {
console.log('Mouse over, set Font Awesome of ID ' + data.originalTarget.id + " here");
});
element.addEventListener('mouseout', function(data) {
console.log('Mouse out, remove Font Awesome of ID ' + data.originalTarget.id + " here");
});
}
var event = new MouseEvent('mouseover', {
'view': window,
'bubbles': true,
'cancelable': true
});
<a class="level-item like-icon">
<div class="icon is-medium">
<i id="hoverIcon1" class="hover-icon far fa-heart">ICON1</i>
<i id="hoverIcon2" class="hover-icon far fa-heart">ICON2</i>
</div>
</a>
I want to drag and drop the content of one div to other div and all the div have the same classname. Also I want to do it in a sortable manner. In the Html there are many div with similar class and is displayed in multiple column.
HTML CODE:
<div class = "section" style = "padding : 4px;">
<span class = "display" ><span style = font-weight : bold;>Lion
</span></span>
</div>
<div class = "section" style = "padding : 4px;">
<span class = "display" ><span style = font-weight : bold;>tiger
</span></span>
</div>
JS code:
function dragAndDrop(){
$(".section").draggable({
refreshPosition: true,
drag: function(event,ui){
ui.helper.addClass("draggable");
},
stop:function(event,ui){
ui.helper.removeClass("draggable");
}
});
$(".section").droppable({
drop: function (event, ui) {
$(this).find("ddsection").addClass("dddroppable").html("Dropped!");
}
});
}
dragAndDrop();
I think you need jquery sortable to achieve what you want.
https://jqueryui.com/sortable/#connect-lists
I have two divs, each containing a list of quantities and items. The items are draggable and the div containing the items is droppable. If an item of the same name appears in that div, that item cannot be dropped on that div again. Eventually I will be controlling the number of items that are moved, but I'm just trying to get this part to work for now.
The problem I'm having is that once I drop an item on the div, I can no longer drag it. I've tried adding draggable() to the new item that I am creating, but that just made things weird.
The exact same code worked when I was adding an li to a ul, but I wanted to move just the name of the item and not the quantity, so I am using divs instead of li.
Here is a jsfiddle of my code: http://jsfiddle.net/imjustabill/eJ4KH/
And here are the pertinent parts
$(function () {
$("#inventory1 .item").draggable({
appendTo: "body",
helper: "clone"
});
$("#inventory2").droppable({
drop: function (event, ui) {
var item = ui.draggable.attr("data-item");
var qty = ui.draggable.attr("data-qty");
var itemDesc = ui.draggable.text();
if (!$(this).find("[data-item='"+item+"']").length) {
$("<div class='qty'></div>").text(qty).appendTo(this);
$("<div class='item ui-draggable' data-qty='"+qty+"' data-item='"+item+"'></div>").text(itemDesc).appendTo(this);
}
}
});
$("#inventory2 .item").draggable({
appendTo: "body",
helper: "clone"
});
$("#inventory1").droppable({
drop: function (event, ui) {
var item = ui.draggable.attr("data-item");
var qty = ui.draggable.attr("data-qty");
var itemDesc = ui.draggable.text();
if (!$(this).find("[data-item='"+item+"']").length) {
$("<div class='qty'></div>").text(qty).appendTo(this);
$("<div class='item ui-draggable' data-qty='"+qty+"' data-item='"+item+"'></div>").text(itemDesc).appendTo(this);
}
}
});
});
<div id="inventory1">
<h4>Inventory 1</h4>
<div class="qty">7</div><div class="item" data-qty="7" data-item="item1">Item 1</div>
<div class="qty">5</div><div class="item" data-qty="5" data-item="item2">Item 2</div>
<div class="qty">2</div><div class="item" data-qty="2" data-item="item3">Item 3</div>
<div class="qty">9</div><div class="item" data-qty="9" data-item="item4">Item 4</div></div>
<div id="inventory2">
<h4>Inventory 2</h4>
<div class="qty">4</div><div class="item" data-qty="4" data-item="item1">Item 1</div>
<div class="qty">2</div><div class="item" data-qty="2" data-item="item5">Item 5</div></div>
Any ideas? Thank you.
The Problem is, that the Draggable calls it's destroy() on drop so you loose your dragging ability. One dirty way is to make your dropped element draggable again:
$("#inventory2").droppable({
drop: function (event, ui) {
var item = ui.draggable.attr("data-item");
var qty = ui.draggable.attr("data-qty");
var itemDesc = ui.draggable.text();
if (!$(this).find("[data-item='"+item+"']").length) {
$("<div class='qty'></div>").text(qty).appendTo(this);
$("<div class='item ui-draggable' data-qty='"+qty+"' data-item='"+item+"'></div>").text(itemDesc).draggable({appendTo: "body", helper: "clone"}).appendTo(this);
}
}
});
But it won't be droppable again.
Do you have to use draggable/droppable?
Just use jQueryUI's own Sortable with connected lists. Its already perfectly implemented to jQueryUI:
jQueryUI Sortable with connected lists
I have a sortable element of div
<div class="question-constructor multiple-choice-problem">
<label for="textbox" class="question-label"> #. Edit this to define question: </label>
<input type="radio" name="test" id="option-1-1" class="question-radio" style="float:left;">
<input type="radio" name="test" id="option-1-2" class="question-radio" style="float:left;">
<input type="radio" name="test" id="option-1-3" class="question-radio" style="float:left;">
<input type="radio" name="test" id="option-1-4" class="question-radio" style="float:left;">
</div>
This divs are draggable then are dropped to the sortable. Here is the div where they drop the draggable:
<div id="preview" ></div>
Here is my sortable JS:
var sortableIn = 0;
$('#preview').sortable({
receive: function(event, ui) {
sortableIn = 1;
$(ui.item).addClass('editable');
},
placeholder: 'ph',
over: function(event, ui) { sortableIn = 1; },
out: function(event, ui) { sortableIn = 0; },
start: function( event, ui ) {
$(ui.helper).addClass('on-sort');
},
beforeStop: function (event, ui) {
if (sortableIn == 0) {
ui.item.remove();
}
},
stop: function( event, ui ) {
$(this).find('.on-sort').removeClass("on-sort");
}
});
What I want to do is create an onclick even to the sortable items specificaly. I tried using this:
$(function() {
$(document).on('click', '#preview ', function() {
console.log('clickable');
});
});
But when I clicked the sortable items It clicks every thing. I want to change the text of the label on the sortable div where I specifically clicked.
For example:
I dragged .question-constructor div 3 times to the #preview div. (This will make the #preview sortable div have 3 items)
When I click the 2nd item in the sortable div, I want to change the text label on it to another value( only on this item ).
How can I do this?
If my question is unclear please comment below and I will respond accordingly.
You could still use the on click event but over the clicked item:
$(".question-label").on("click", function (e) {
$(e.target).text('I am changed');
});
Fiddle: http://jsfiddle.net/eBXkT/
i have this drag drop code for my elements
$( ".sortable" ).sortable({
revert: true
});
$( ".draggable" ).draggable({
connectToSortable: ".sortable",
helper: "clone", //clone
revert: "true"
});
$( ".sortable" ).droppable({
drop: function( event, ui ) {
var $element = $(ui.draggable).clone();
$element.find('.control').children('.delete').css('display', 'inline').click(function () {
$(this).parent().remove();
});// display properties Link
$element.find('.control').children('.properties').css('display', 'inline');
//For Text Box Change the text and add a label
if($element.find('.control').find('input').attr('type') == "text")
{
$element.find('.control').find('.controlText').html("");
$element.find('.control').find('label').html("Label Here");
}
}
});
This is the code for the drag element
<div class="tools">
<ul>
<li class="draggable" >
<div class="control">
<label> </label>
<input type="text" name="txt" value="" /><span class="controlText"> Text Box </span>
<div class="delete" style="display:none"><sup>x</sup></div>
<div class="properties txtbox" style="display:none">Properties</div>
</div>
</li>
</ul>
</div>
When the Text Box is dragged this event is attached to it
$('.txtbox').live('click', function() {
//get the label
var label = $(this).parent().find('label').html();
$("#textbox_label").val(label);
$( "#dialog-textbox" ).dialog( "open" ).data('parent_div',$(this).parent());
return false;
});
This is the dialog which opens when the Properties is clicked
<div id="dialog-textbox" title="Text Box Properties" style="display:none">
<p>This is the Text Box Properties Form.</p>
<form>
<fieldset>
<label for="textbox_label">Enter Label </label>
<input type="text" name="textbox_label" id="textbox_label" class="text ui-widget-content ui-corner-all" />
</fieldset>
</form>
The following code handles the Dialog
$("#dialog-textbox").dialog({
autoOpen: false,
height: 300,
width: 350,
modal: true,
buttons: {
"Apply": function(){
//code to update element goes here...
var label = $("#textbox_label").val()
var $elem_clicked = $("#dialog-textbox").data('parent_div'); //This retrieves
$elem_clicked.find('label').html(label);
$( this ).dialog( "close" );
},
Cancel: function() {
$( this ).dialog( "close" );
}
}
});
What is happening here is that i have a text box which i drag to a ul li list and that is sortable, the text box is added to the list and and then i show and close and properties links attached to the text box. when the properties is clicked a dialog box opens and asks for a new label. user gives new label to the text box that is shown with that. But what goes wrong is when i again drag the box in the sortable list up or down it goes back to its initial state and the new label is lost... I think this is due to the helper clone or should i make a clone of the draggable item?
Use a simple trick to distinguish you original element fromthe copy you drag:
- add a class to the label of the original element eg
<label class='orig'> </label>
- in your $(".sortable").droppable handler add atthe end
$(ui.draggable).find('.control').find('.orig').html("Label Here").removeClass('orig');
It should solve your problem. Check it here.