Enhancing a Jquery drag-and-drop demo - javascript

I have a requirement that very closely matches this Jquery demo, which is a simple shopping cart demo. Basically I need to make two enhancements into this demo.
I need a text input alongwith with the available "products". So when I drag-drop one of the products, the text fields should be dragged along with that, which will be filled by user in the "cart" field.
I need a "cross" in front of each item in cart, which can be used to delete a certain item. Jquery's "destroy" function doesn't seem to do that. So how can I accomplish deletion of items from shopping cart?
Thanks for all the help.

You can specify your own "helper" on the draggable definition. Whatever html you specify as your helper is what will be shown during the drag animation. In your sortable (the drop area in the demo) you can override the beforeStop function. In there you can replace the item with whatever you want to be actually dropped into your shopping cart. In that, you could add the X button with javascript or something to remove the item
I recently implemented this to drag from a datatable into a list, so had to convert the tr into an li. It works in jQuery 1.4 but I get strange results on the drop when I drag out of the top in 1.5, and I haven't resolved that yet. Here's my helper defintion
helper: function() {
var text = this.children[0].innerText;
var result = "<li id='"+this.id+"'>"+text+"</li>";
return result;
},
and here's my beforeStop function
beforeStop: function( event, ui ) {
var id = ui.helper.attr( "id" );
var text = ui.helper.text();
var li = "<li id='"+id+"'>"+text+"</li>";
$(ui.item).replaceWith( li );
}

Related

Strange behavior to searchable JQuery Connected Sortable. Need fix

I am using a connected sortable that saves wonderfully via asp.net back-end on a button click. Additional features include the ability to search each of the lists. This works well until you do the drop and drag of an item from one list to another. When I do the search of the one list using JavaScript to search items in the one list, it still thinks that it is a part of the first list. Has anyone seen this behavior? and do you know of a possible fix to make item permanently part of the said dragged upon list within the DOM. This behavior is in FF, IE, Chrome, etc.
Now I do have buttons on this list that move items from one list to the other based on them being selected then button clicked, it is then a part of the second list using JQuery append();. This makes the item a permanent part of the second list's DOM and is able to be searched upon within that list.
So here is my "Ah-Ha" moment. It was doing what it was supposed to do looking for the item and showing it and if it was not the item it wouldn't show it. however when the item moved to the other column using sortable it still had the same class as the first column. I needed to make that switch.
The answer is here as follows...
http://jsfiddle.net/6jmLvysj/
$(input)
.change(function () {
var filter = $(this).val();
if (filter) {
//here is my DAH moment it is looking for this class so when the user was typing in the first search input it was looking for item it would be searching for it is the second column--give myself a Gib slap--
$(list).find(".ui-state-default:not(:Contains(" + filter + "))").hide();
$(list).find("ui-state-default:Contains(" + filter + ")").show();
} else {
$(list).find(".ui-state-default").show();
}
return false;
})
.keyup(function () {
$(this).change();
});
then this
//so here is where I changed this code when the user brings the item over then it changes it to the proper respected class depending where the user drops it so it can be searched.
stop: function (event, ui) {
if (ui.item.hasClass("ui-state-default")) {
ui.item.attr('class', 'ui-state-highlight');
}
else {
ui.item.attr('class', 'ui-state-default');
}

Combining jQuery UI's draggable, selectable and droppable

I'm trying to combine jQuery UI's draggable, droppable, selectable AND sortable libraries, to create a web interface for drag-selecting elements and dropping them into another area. The user will be able to remove items from the droppable area should they need to, and also control/cmd + click to select specific items without dragging.
So far I've made a pretty decent effort, but I'm having issues when it comes to adding "removed" items back into the original list of selectable items. The drag-selecting works well with these items (that are re-created dynamically), but my main problem is when control/cmd + clicking items once they have been added back to the original list.
To recreate the issue, add items to the second droppable area and then remove them (by clicking on them). Once the items are back in the original list, control/cmd + clicking the items you deleted doesn't select them properly, so when you go to drag these items, only one is added (instead of the correct amount).
Here's a jsfiddle: http://jsfiddle.net/b8key7Lw/
And here's the code I'm having difficulty getting to work once the #selectable > div items are dynamically created (around line 45 in the jsfiddle):
$("body").on("click", "#selectable > div", function(e){
if (e.metaKey == false) {
$( "#selectable > div" ).removeClass("ui-selected");
$(this).addClass("ui-selecting");
}
else {
if ($(this).hasClass("ui-selected")) {
$(this).removeClass("ui-selected");
}
else {
$(this).addClass("ui-selecting");
}
}
$( "#selectable" ).data("ui-selectable")._mouseStop(null);
});

Conditional jQuery Toggle Function

I want to hide and show list items based on their attributed class.
The problem is that certain list items have multiple classes. So if I toggle one class then toggle another, any items with both selected classes will be removed.
I created a demo of my problem: http://jsfiddle.net/a4NkN/2/
Here's the JS CODE:
$('#easy').click(function(){
$(this).toggleClass( "checked" );
$('.easy').toggle();
});
$('#fun').click(function(){
$(this).toggleClass( "checked" );
$('.fun').toggle();
});
$('#silly').click(function(){
$(this).toggleClass( "checked" );
$('.silly').toggle();
});
If you select the "Easy" and "Fun" buttons, Boating will disappear.
How can I get Boating to stay?
This might be a good point to start from. Although you can do this cheaper and cleaner, that gives the idea:
Use an array to save the status of your selection buttons and one corresponding to hold the class names. Whenever your select buttons get clicked, you set all your elements invisible and reset those visible again, that are already selected by other buttons or the currently clicked, which is all saved in the switcher array.
//saves whether button is active
var switcher = [false, false, false];
//holds your classes selectable
var classes = ['easy', 'fun', 'silly'];
$('.toggler').click(function () {
// toogle the select button and save status
var x = $(this).hasClass('checked');
switcher[$(this).data('switch')] = !x;
$(this).toggleClass("checked", !x);
// iterate through your elements to set them active if needed
$('li').each(function () {
var cur = $(this);
cur.addClass('hidden');
$.each(switcher, function (index, data) {
if (data && cur.hasClass(classes[index])) {
cur.removeClass('hidden');
}
});
});
});
Whole solution in this fiddle: http://jsfiddle.net/BMT4x/
You cannot unconditionally toggle elements based on a click on one of the button filters if it is possible for an element to satisfy multiple filters at once. The correct approach is a little more involved.
Since your checked class means that only items corresponding to that button should be shown, do exactly this: toggle them based on the current status of the button. The items should be shown if and only if the corresponding button is checked as a result of clicking it.
$('#easy').click(function(){
$(this).toggleClass( "checked" );
$('.easy').toggle($(this).is(".checked"));
});
This code uses the last version of .toggle, the one accepting a boolean argument.
It can also be done more succintly:
$('.easy').toggle($(this).toggleClass( "checked" ).is(".checked"));

Disable jQuery UI draggable if element has particular class

I have an interactive basket whereby the user can drag and drop an item into the basket. However, the same item cannot be placed in the basket twice (but it remains visible, albeit faded), so once it is in the basket, the draggable property must be disabled.
I tried doing this:
$("#product_badges li:not(.on)").draggable({
// Options here
});
However, as this just initiates the draggable() property, the element is still draggable even if I do add the on class to it when it has been dropped.
I therefore cannot see a way of achieving this without having several instances for the draggable property like so:
$("#product_badges li:not(.on)").each(function(){
$(this).draggable({
// Options here
}
});
With the above method I can then call the individual product_badge and disable dragging like so:
This only gets called once the item has been placed into the basket (dropped)
$('.item',$('#product_badges')).draggable('disable');
Is there a better way of achieving this? Can you set an HTML element to only be draggable if it does not have a particular class?
Update
See example here: http://jsfiddle.net/mB6GK/2/
use the cancel property.
$("#product_badges li").draggable({ cancel: ".no" })
fiddle:
http://jsfiddle.net/sGguz/
You could use the start event,
$("#container").draggable({
start: function( event, ui )
{ return !$(ui.helper).hasClass('nodrop'); }});
http://jsfiddle.net/mB6GK/4/

jQuery UI checkboxes misbehaving when cloned

I'm trying to create a table of inputs that automatically adds a new row when you enter text in one of the inputs on the bottom line. For the most part, it works fine. However, I'm having some trouble with jQuery UI checkbox buttons.
The checkbox buttons are supposed to change their icon when clicked. This works fine for the original buttons, but the cloned button that appears when you add a new row doesn't work properly.
You can see it in jsfiddle here. To replicate the issue, put some text in the third input down. You'll see that a fourth row appears. If you press the fourth checkbox, you'll see the third checkbox is the one whose icon changes. The wrong button also gets ui-state-focus but doesn't actually get focus, which really baffles me, though the correct button does get ui-state-active and seems, as far as I can tell, to evaluate as having been checked properly.
To be clear, the two checkboxes do not have the same ID, and their labels are for the right checkbox - the createNewRow() function takes care of that. If you comment out the line that turns the checkboxes into jQuery UI checkboxes, you'll see everything works fine. If you console.log the value of $(this).attr('id') in the buttonSwitchCheck function, you'll see that it has the right ID there too - if you click the fourth button, it'll tell you that the id of $(this) is "test4", but it's "test3" (the third button) that gets the icon change.
I'm going mad staring at this and I'd appreciate any help people can give. Here's the code:
// Turns on and off an icon as the checkbox changes from checked to unchecked.
function buttonSwitchCheck() {
if ($(this).prop('checked') === true) {
$(this).button("option", "icons", {
primary: "ui-icon-circle-check"
});
} else {
$(this).button("option", "icons", {
primary: "ui-icon-circle-close"
});
}
}
// Add a new row at the bottom once the user starts filling out the bottom blank row.
function createNewRow() {
// Identify the row and clone it, including the bound events.
var row = $(this).closest("tr");
var table = row.closest("table");
var newRow = row.clone(true);
// Set all values (except for buttons) to blank for the new row.
newRow.find('.ssheet').not('.button').val('');
// Find elements that require an ID (mostly elements with labels like checkboxes) and increment the ID.
newRow.find('.ssheetRowId').each(function () {
var idArr = $(this).attr('id').match(/^(.*?)([0-9]*)$/);
var idNum = idArr[2] - 0 + 1;
var newId = idArr[1] + idNum;
$(this).attr('id', newId);
$(this).siblings('label.ssheetGetRowId').attr('for', newId);
});
// Add the row to the table.
newRow.appendTo(table);
// Remove the old row's ability to create a new row.
row.removeClass('ssheetNewRow');
row.find(".ssheet").unbind('change', createNewRow);
}
$(document).ready(function () {
// Activate jQuery UI checkboxes.
$(".checkButton").button().bind('change', buttonSwitchCheck).each(buttonSwitchCheck);
// When text is entered on the bottom row, add a new row.
$(".ssheetNewRow").find(".ssheet").not('.checkButton').bind('change', createNewRow);
});
EDIT: I was able to find a solution, which I'll share with the ages. Thanks to "Funky Dude" below, who inspired me to start thinking along the right track.
The trick is to destroy the jQuery UI button in the original row before the clone, then reinitializing it immediately afterwards for both the original row and the copy. You don't need to unbind and rebind the change event - it's just the jQuery UI buttons which have trouble. In the createNewRow function:
row.find('.checkButton').button('destroy');
var newRow = row.clone(true);
row.find('.checkButton').add(newRow.find('.checkButton')).button().each(buttonSwitchCheck);
Try using the newer method .on, that allows for delegation, which should help with the dynamic changes to your DOM:
$(".checkButton").button().each(buttonSwitchCheck);
$("table").on("change", ".checkButton", buttonSwitchCheck);
I'm not sure, but it might help with not having to worry about binding events to specific elements.
Also, you could use it for the textbox change event:
$("table").on("change", ".ssheetNewRow .ssheet:not(.checkButton)", createNewRow);
Here's your fiddle with my changes: http://jsfiddle.net/Cugb6/3/
It doesn't function any different, but to me, it's a little cleaner. I thought it would've fixed your problem, but obviously hasn't, due to problems with the button widget.
And funny enough, it doesn't seem they "support" cloning: http://bugs.jqueryui.com/ticket/7959
i think you are using deep clone, which also clones the event handler. in your create new row function, try unbinding the change event then rebind on the clone.

Categories

Resources