I am looking for an example of drag and drop where the drag item has two distinct areas that have to match up with two droppable areas.
Example:
I would like the blue drag item to revert unless it is dropped in a position where each of its red children land on a green area.
Ideally I would like to use jquery ui (as I have experience with it), but any javascript library would be fine, thanks in advance.
You can accomplish this by using a combination of draggable/droppable options. Given HTML like this:
<div id="blue" class="valid">
<div id="red-one" class="red"></div>
<div id="red-two" class="red"></div>
</div>
<div id="green-container">
<div id="green-one" class="green">
</div>
<div id="green-two" class="green">
</div>
</div>
(omitting CSS, I did add some rules, see in the fiddle below).
You can write JavaScript like this:
function isInside(one, other) {
return one.offset().left >= other.offset().left &&
one.offset().top >= other.offset().top &&
one.offset().top + one.height() <= other.offset().top + other.height() &&
one.offset().left + one.width() <= other.offset().left + other.width();
}
$("#blue").draggable({
drag: function(event, ui) {
var $this = $(this);
var $reds = $this.children(".red");
var $greens = $("#green-container").children(".green");
var firstRed = $reds.first();
var firstGreen = $greens.first();
var secondRed = $reds.last();
var secondGreen = $greens.last();
if (isInside(firstRed, firstGreen) && isInside(secondRed, secondGreen)) {
$this.addClass('valid');
}
else {
$this.removeClass('valid');
}
},
revert: 'invalid'
});
$("#green-container").droppable({ accept: ".valid" });
Check it out here: http://jsfiddle.net/andrewwhitaker/g6FKz/
Notes:
For some reason I had to apply the 'valid' class initially to the 'blue' div, or else the target droppable would not accept the dragged element, even if it was valid (would appreciate some input on this). Not sure what's up with that, might be a bug in jQueryUI. Not a huge deal though.
The target droppable isn't exactly the two green elements, it's a white div that contains those elements. This should be clear from the example.
Every time you move the draggable div, the "drag" event is called, which determines if the red boxes are inside the green ones and assigns a valid class to the draggable div. The droppable object only accepts valid draggables.
Hope that helps!
Related
I am trying to update a c3.js chart using drag and drops with dragula.js, but I don't know how to get the id of the div that is dragged into a new container. My html is something like this:
<div id="collapse1" class="panel-collapse collapse">
<div id="color1" class="form-inline">1</div>
<div id="color2" class="form-inline">2</div>
<div id="color3" class="form-inline">3</div>
</div>
<div id="collapse2" class="panel-collapse collapse">
</div>
and I'm using dragula.js to drag and drop:
dragula([collapse1,collapse2]);
I am really new to jquery, but following this question, to access the id of the <div> dropped into collapse2 in I was trying to do something like this:
alert($("#collapse1.collapse2 div:first").attr("id"));
But no results. Any help would be really appreciated
Dragula has three Elements One is Source Div, Target Div and Its associated Element. Following Method Works For Me as Charm except i am Not using get() method which has version issue.
You Can Try Both.
Dragula gives you the id of dropped div, Source Div, Target Div.
const dragula = Dragula(['', '']);
dragula.on('drop', (el, target, source, sibling) => {
const elementId = $(el).attr("id");
const targetID = $(target).attr("id");
const sourceId = $(source).attr("id");
}
Can't answer the question directly because I am not familiar with dragula. However, I have used jqueryUI drag drop extensively and its a really good tool. You might want to give that framework a try.
Since you asked for an example, I dug into some of my old code. You might want to go look through the jqueryUI draggable and droppable tutorials to give you some background before looking at this. I have included parts of a function. I put little dots to show you where code has been left out. I have put <<< next the key lines for you. Notice how I use closure to make references available across different parts. Closure is soooo awesome. I abuse the death out of it, so learn how to use it if you can.
Note that once I got my drag object, that is what you are asking for. Notice how I reference the variable to my function later when I register the draggable.
Btw, notice there is also a stop drag function referenced which I don't show the definition of. If you move the declaration of the dragObject outside of startDrag then you can also see it from stopDrag since the definition of the function is "enclosed" in the outside register function.
function tapeChart_registerDraggables(parentObject,scope) {
if ((parentObject==null)||(parentObject==undefined)) {
parentObject=$jq(document.body);
}
var availablesShow = false;
var savingToServer = false;
var dragClone = null;
var startDrag = function(event, ui) {
tapeChartDraggingReservation = true;
var dragObject = event.target; <<<<<<
if (dragObject.getAttribute("unassigned")=="true") {
var is_chrome = window.chrome;
var is_safari = navigator.userAgent.toLowerCase().indexOf('safari/') > -1;
if (!is_chrome && !is_safari) {
$(ui.helper).css("margin-left", event.clientX - $(dragObject).offset().left);
$(ui.helper).css("margin-top", event.clientY - $(dragObject).offset().top);
}
}
...
// assigned rooms
if (scope!="UNBLOCKED") {
// register items in the grid
$(parentObject).find( ".NODRAGHELPER" ).draggable(
{
snap : "true",
revert : "invalid",
start: startDrag, <<<<
stop: stopDrag
}
)
.click(function(){
if ( $(this).is('.NODRAGHELPER-dragging') ) {
return;
}
// seems that the user can drop and click fast
// prevent this
if (!savingToServer) {
tapeChart_getReservation(this);
}
return false;
});
}
...
After a long search I didn't find what I am looking for, or if I did, it didn't work for what I am doing or I didn't know how to use it properly. In all cases I really need your help in this matter.
What I did is defining my processing div by only identifying the div(s) with ID
then the rest is written in jQuery. There is a lot of code, so I'll put up a shortened version.
HTML
<div id="blue" class='processing'>
<div class="bar"></div>
<div class="text">BLUE</div>
</div>
<div id="red" class='processing'>
<div class="bar"></div>
<div class="text">RED</div>
</div>
<div id="yellow" class='processing'>
<div class="bar"></div>
<div class="text">YELLOW</div>
</div>
jQuery
$(".processing").click(function () {
var $this = $(this);
var ID = $this.attr('id');
switch(ID){
case "blue":
var name = $this.find(".text").text();
var width = 60;
// some other var(s)
break;
case "red":
var name = $this.find(".text").text();
var width = 40;
// some other var(s)
break;
case "yellow":
var name = $this.find(".text").text();
var width = 20;
// some other var(s)
break;
}
$this.find(".text").text("THE " + name);
$this.find(".bar").css("width", width + "%");
// some other complicated coding
});
Explanation of what the code does
This is the simple version, the html for all div(s) is the same but the IDs are different and the text div also. Once the div is clicked, the code will get the ID then match it with the switch to get the right variables for it, (there are a lot more variables than this). Let's say that the div marked with the ID blue is clicked, the div marked with class bar inside blue, its width will change to 60% according to the switch. If red was clicked, the bar's width for red will change to 40% and so on.
All the important variables are inside the click function. what I want is...
What I want
Before clicking, I want the user to be able to hover the mouse over the bar and next to the text the parentage will show up without clicking, then disappear once the mouse is away.
I know how to do that with functions, using mouseenter, and mouseleave.
The problem
the problem is... I'll have to use the variables 3 times. For mouseenter, mouseleave and for click. How could I do that or what should I do with the variables so I will be able to use them inside these three functions without repeating them.
The processing div is a rectangle,
the bar div is the background,
and the text div is the text over the processing bar.
Picture to explain: http://i.imgur.com/nFSrvy6.png
1 Reminder
In this example the var I want to use in all functions is width. But in the code I have (long version), not just one variable.
Why don't you declare all your variables inside an object:
var data = {
blue: {
width: 60,
// other variables
},
red: {
width: 40,
// other variables
},
yellow: {
width: 20,
// other variables
}
}
Then you can use your data object wherever you need it:
var ID = $this.attr('id');
var width = data[ID].width;
Robby's answer is good. I'm also a big fan of the data attribute for embedding element-specific stuff right into the element.
I'm also using the data attribute to mark the element as clicked when it's been clicked, so that mouseover and mouseleave can look for that and have different behaviors when it's already clicked (if any at all).
<div id="blue" class="processing" data-width="60">
<div class="bar"></div>
<div class="text">BLUE</div>
</div>
<div id="red" class="processing" data-width="40">
<div class="bar"></div>
<div class="text">RED</div>
</div>
<div id="yellow" class="processing" data-width="20">
<div class="bar"></div>
<div class="text">YELLOW</div>
</div>
$(".processing").on("mouseover", function () {
if( ! $(this).data("clicked")){
//if it hasn't been clicked yet, do something
}else{
//otherwise, leave it alone
}
});
$(".processing").on("mouseleave", function () {
if( ! $(this).data("clicked")){
//if it's already clicked, leave it alone
}else{
//otherwise, revert the changes from mouseover
}
});
$(".processing").on("click", function () {
var name = $(this).find(".text").text();
var width = $(this).data("width");
$(this).find(".text").text("THE " + name);
$(this).find(".bar").css("width", width + "%");
$(this).data("clicked", true); //mark the div as clicked
// some other complicated coding
});
I'm trying to change the style of an element using javascript on a keydown event. I've already done it with mouse clicks like this :
#mpc ul li:active {
background:#844;
And these are the original properties of the element im trying to change:
<div id="mpc">
<div id="wrapper">
<ul>
<li id="kickDrum_p1">1<p>Kick</p></li>
<li id="snareDrum_p2">2<p>Snare</p></li>
<li id="closedHiHat_p3">3<p>ClosHat</p></li>
<li id="openHiHat_p4">4<p>Open HiHat</p></li>
</ul>
</div>
</div>
This is the original css.
#mpc ul li {
background:#999;
}
I'm really new to css/html and javascript and haven't started learning jquery so I prefer doing it all by javascript and css.
The idea is to assign a different key to every list item and when that key is pressed on the page, the list item's background changes, just like I've done the click event. The difference with the click event is I've made it so it doesn't follow the id's since you manually select the list item you want and then it changes it's style.
With key input however I need to specify which list item exactly I'm targeting.
So I think I need to create a new css class with the properties of the background I want the element to change to and using javascript to tell that when I press the 'H' key for example it should change the element's style to that and when I let go of the key to reverse it back to normal. If that makes sense. I do not know how to ahcieve that.
EDIT:
This is how I tried to do it:
var keypress = document.getElementById("kickDrum_p1")[0]
document.addEventListener("keydown", function (e) {
if(e.keyCode == 69) {
var key = e.keyCode
var color = keypress.getElementByClassName("color")
if(color.length !=0 ){
cur = color[0]
cur.className = " "
}
cur.className="color"
}
});
And in the styles I have this:
#mpc ul li.color {
background:#844;
}
<P ID="MyID" CLASS="Testclass">Test</P>
<SCRIPT>
Add here jQuery
$("*").keypress(function(event){
if(event.which==107 || event.which==75){
$("#MyID").attr("class", "newclass");
}
}
</SCRIPT>
BTW: 107 and 57 are the K and the k in the ASCII table.
Note: Changed code so that images and texts are links.
Basically, I have 3 pictures all with the same class, different ID. I have a javascript code which I want to apply to all three pictures, except, the code needs to be SLIGHTLY different depending on the picture. Here is the html:
<div class=column1of4>
<img src="images/actual.jpg" id="first">
<div id="firsttext" class="spanlink"><p>lots of text</p></div>
</div>
<div class=column1of4>
<img src="images/fake.jpg" id="second">
<div id="moretext" class="spanlink"><p>more text</p></div>
</div>
<div class=column1of4>
<img src="images/real.jpg" id="eighth">
<div id="evenmoretext" class="spanlink"><p>even more text</p></div>
</div>
Here is the Javascript for the id="firsttext":
$('#firstextt').hide();
$('#first, #firsttext').hover(function(){
//in
$('#firsttext').show();
},function(){
//out
$('#firsttext').hide();
});
So when a user hovers over #first, #firsttext will appear. Then, I want it so that when a user hovers over #second, #moretext should appear, etc.
I've done programming in Python, I created a sudo code and basically it is this.
text = [#firsttext, #moretext, #evenmoretext]
picture = [#first, #second, #eighth]
for number in range.len(text) //over here, basically find out how many elements are in text
$('text[number]').hide();
$('text[number], picture[number]').hover(function(){
//in
$('text[number]').show();
},function(){
//out
$('text[number]').hide();
});
The syntax is probably way off, but that's just the sudo code. Can anyone help me make the actual Javascript code for it?
try this
$(".column1of4").hover(function(){
$(".spanlink").hide();
$(this).find(".spanlink").show();
});
Why not
$('.spanlink').hide();
$('.column1of4').hover(
function() {
// in
$(this).children('.spanlink').show();
},
function() {
// out
$(this).children('.spanlink').hide();
}
);
It doesn't even need the ids.
You can do it :
$('.column1of4').click(function(){
$(this); // the current object
$(this).children('img'); // img in the current object
});
or a loop :
$('.column1of4').each(function(){
...
});
Dont use Id as $('#id') for multiple events, use a .class or an [attribute] do this.
If you're using jQuery, this is quite easy to accomplish:
$('.column1of4 .spanlink').hide();
$('.column1of4 img').mouseenter(function(e){
e.stopPropagation();
$(this).parent().find('.spanlink').show();
});
$('.column1of4 img').mouseleave(function(e){
e.stopPropagation();
$(this).parent().find('.spanlink').hide();
});
Depending on your markup structure, you could use DOM traversing functions like .filter(), .find(), .next() to get to your selected node.
$(".column1of4").hover(function(){
$(".spanlink").hide();
$(this).find(".spanlink, img").show();
});
So, the way you would do this, given your html would look like:
$('.column1of4').on('mouseenter mouseleave', 'img, .spanlink', function(ev) {
$(ev.delegateTarget).find('.spanlink').toggle(ev.type === 'mouseenter');
}).find('.spanlink').hide();
But building on what you have:
var text = ['#firsttext', '#moretext', '#evenmoretext'];
var picture = ['#first', '#second', '#third'];
This is a traditional loop using a closure (it's better to define the function outside of the loop, but I'm going to leave it there for this):
// You could also do var length = text.length and replace the "3"
for ( var i = 0; i < 3; ++i ) {
// create a closure so that i isn't incremented when the event happens.
(function(i) {
$(text[i]).hide();
$([text[i], picture[i]].join(',')).hover(function() {
$(text[i]).show();
}, function() {
$(text[i]).hide();
});
})(i);
}
And the following is using $.each to iterate over the group.
$.each(text, function(i) {
$(text[i]).hide();
$([text[i], picture[i]].join(', ')).hover(function() {
$(text[i]).show();
}, function() {
$(text[i]).hide();
});
});
Here's a fiddle with all three versions. Just uncomment the one you want to test and give it a go.
I moved the image inside the div and used this code, a working example:
$('.column1of4').each(function(){
$('div', $(this)).each(function(){
$(this).hover(
function(){
//in
$('img', $(this)).show();
},
function(){
//out
$('img', $(this)).hide();
});
});
});
The general idea is 1) use a selector that isn't an ID so I can iterate over several elements without worrying if future elements will be added later 2) locate the div to hide/show based on location relational to $(this) (will only work if you repeat this structure in your markup) 3) move the image tag inside the div (if you don't, then the hover gets a little spazzy because the positioned is changed when the image is shown, therefore affecting whether the cursor is inside the div or not.
EDIT
Updated fiddle for additional requirements (see comments).
<div id="content">
<div class="oddpost">
<div class="arrow"></div>
</div>
<div class="oddpost">
<div class="arrow"></div>
</div>
<div class="oddpost">
<div class="arrow"></div>
</div>
<div class="oddpost">
<div class="arrow"></div>
</div>
</div>
$(function() {
if (($(".oddpost").position().left + $(".oddpost").width()) >= $("#content").width()) {
$('.arrow').hide();
}
});
http://jsfiddle.net/Ek5Gy/52/
In the code, I have a div(.arrow) nested on another div(.oddpost). What I want to do is hide the .arrow of the left .oddpost only. The idea is .arrow hides when .oddpost is near the left side of #content.
I've tried using offset but it gives the same offset().left value on all the oddpost div so all the arrow div still hides, even the one on the right.
Can anybody tell me how to fix this?
Your main problem lies in your selector usage:
// selecting stuff
$(".oddpost")
// doing stuff on the selection
.position().left
But, you'll have to know how those helper methods work on the given set. position (and many others) will only work on the very first element in the set. Not on every element.
So what you have to do is, iterate over each element in the set and do the test for every element independently, like:
$(".oddpost").each(function () {
if ($(this).prev().length === 0 || $(this).offset().left < $(this).prev().offset().left) {
$('.arrow', this).hide();
}
});
// or
$(".oddpost").each(function () {
if ($(this).position().left === 0) {
$('.arrow', this).hide();
}
});
http://jsfiddle.net/Ek5Gy/53/
The position() call returns values for the first selected element (it doesn't work on all selected elements as many other jQuery calls). Check Javadoc.
This is working for me:
$(function(){
$(".oddpost").each (function (idx, el) {
if ($(el).position().left < $(el).width()) {
$('.arrow', el).hide();
}
});
});
The modified jsfiddle: http://jsfiddle.net/Ek5Gy/54/
In your code, $('.arrow').hide() will always act on all DOM elements that match that particular selector (.arrow). Also, your calculations are only performed once, for the first .oddpost element, since that's the behavior of the position() jQuery function.
I just did not follow exactly what you meant by "near the left of content". I've altered your code a bit to reflect all these changes and prepared a little jsFiddle. Check it here.
Your example works fine.
In your example the value of:
$(".oddpost").position().left + $(".oddpost").width() // Equals 100
This is less the total of #content which is 270px. Your asking if it's greater than or equal too. If you want to hide the arrow when it's near the left try this:
if ($(".oddpost").position().left === 0) {
$('.arrow').hide();
}
EDIT:
$(function(){
$(".oddpost").each(function(){
var position = $(this).position().left;
alert (position);
if(position === 0){
// Do what you need to here.
}
});
});
This is what your looking for...