Here's my jsfiddle: http://jsfiddle.net/LUsMb/2779/
What I'm attempting to do is make a checkbox-selectable select2 that shows how many items you have selected, instead of showing the item list. Everything works except for de-selecting an item. I think this is because I call evt.preventDefault in the select2-selecting event. Here is my code for that particular event:
.on('select2-selecting', function(evt) {
var newValue = evt.object.value;
var found = false;
var me = this;
var $me = $(me);
var arr = $me.select2('data');
arr.forEach(function(e) {
if(found) return;
if(newValue === e.value) {
found = true;
var index = arr.indexOf(e);
if(index === -1) console.error(e,"NOT FOUND IN ARRAY. ABORT ABORT ABORT.");
arr.splice(index, 1);
$me.select2('data',arr);
changeText(me);
evt.preventDefault();
}
});
I do evt.preventDefault() because I want to remove the item from the select2 data list instead of adding it again (because I don't want to have it work the traditional way). Unfortunately, when I go to de-select an item it does all of the updating correctly, except for in select2 - the display list is not updated properly. I could always close and re-open the list, but I strongly prefer not to do that.
Has anyone done this successfully before? Or maybe would know where to go from here?
Related
I have a select box. Currently, this select box make an ajax call on change.
Now, I want to make call only when a condition is met.
So, here is my code:
$('#buildingSelect').on('change', function(){
var result = checkDirtyStatus();
//this checkDirtyStatus alert message if there is some changes on the form.
//if cancel return false, if confirm return true.
if(result === false) {
return;
}
//make ajax call
});
This prevents from making ajax call, however, this change the selected option of the select i.e, if option1 is selected at the begining and if I try to select next option then it will change the selected option to option2 then only check the status.
On searching on the internet, I got the option of focusin.
$('#buildingSelect').on('focusin', function(){
// console.log("Saving value " + $(this).val());
var result = checkDirtyStatus();
if(result === false) {
return;
}
}).on('change', function(){
g_building_id = $(this).val();
getAmenitiesDetails(g_building_id);
});
However, using this focusin options makes the alert box to appear everytime no matter either I click cancel or ok. This might be because, it call focusin again whenevr I click Ok or Cancel.
What would be the best option to check this status, and if result is false, I don't want to change the selected option as well.
Update
Answer from marked as duplicate not preventing from changing the selected option. Its making ajax call on click i.e. before checking condition.
CodePen Link
function checkDirtyStatus(){
dirtyStatus = true;
if(dirtyStatus === true){
if (confirm("Changes you made may not be saved.")) {
return true;
}else{
return false;
}
}
}
Finally, by mixing the link from Rory and idea of organizing code from some. I have find a solution for my problem. So, if anyone got stuck on the similar problem here is my solution.
$(function(){
var lastSel;
$('#buildingSelect').on('focusin', function(){
lastSel = $("#buildingSelect option:selected");
}).on('change', function(){
if(!checkDirtyStatus()) {
lastSel.prop("selected", true);
return;
}else{
//made ajax call
//$.ajax({})
}
});
});
function checkDirtyStatus(){
let dirtyStatus = getDirtyStatus();
if(dirtyStatus){
return confirm("Changes you made may not be saved.");
}
return true;
}
Let us look at your function:
function checkDirtyStatus(){
dirtyStatus = true; // I assume this is only for testing
if(dirtyStatus === true){ // This can be simplified.
if (confirm("Changes you made may not be saved.")) {
return true;
}else{
return false;
}
}
}
confirm returns a Boolean that is either true or false, so you can simplify your function like this:
function checkDirtyStatus(){
dirtyStatus = true;
if(dirtyStatus){
return confirm("Changes you made may not be saved.");
}
// Notice that you do not return anything here. That means that
// the function will return undefined.
}
Your other function can be simplified like this:
$('#buildingSelect').on('change', function(){
if(!checkDirtyStatus()){
// Here you probably want to set the value of the select-element to the
// last valid state. I don't know if you have saved it somewhere.
return;
}
//make ajax call
});
I played with your codepen and you have some errors in your selectors. As I get confused by your explanation I will try to explain what you could update and how to use it in your code and I hope this is what you need to solve your problem.
First I would change your js to this:
var lastSel = $("#buildingSelect").val();
$("#buildingSelect").on("change", function(){
if ($(this).val()==="2") {
$(this).val(lastSel);
return false;
}
});
The proper way to get the value of a select box in jquery is with the .val(). In your case you selected the entire selected option element.
I store this value in the lastSel variable. Then in the change function the new value of the select list is $(this).val(). I check against this value and if it equals 2 I revert it to the value stored in the lastSel variable with this $(this).val(lastSel).
Keep in mind that the value of a select list is always a string, if you want to check against a number you must first cast it to a numeric value e.g. by using parseInt.
If you want to use the checkDirtyStatus for the check then you should only call this function in the change and pass as parameters the lastSel and the newSel like this:
$("#buildingSelect").on("change", function(){
checkDirtyStatus(lastSel, $(this).val());
});
Then you can transfer the logic from the change function into the checkDirtyStatus function and do your checks there. In this case if you wish to revert the select value instead of $(this).val(lastSel) you will do a $("#buildingSelect").val(lastSel).
I hope this helps.
I'm using the following code to move options from one drop down to another using jQuery:
$(document).on('change keyup', '.select-groups', function(){
moveSelectOptions($(this), 'available-groups', 'selected-groups');
});
var moveSelectOptions = function(elm, source, target){
var sourceId = source;
var targetId = target;
if (elm.attr('id') != sourceId) {
var sourceId = target;
var targetId = source;
}
return !$('#'+sourceId+' option:selected').remove().appendTo('#' + targetId);
};
I'm using both on change and on keyup events and everything works well, expect when there is only one option left in the dropdown, which in that case the option does not get moved over to the other dropdown.
I tried adding click event too but looks like it's conflicting with the other events.
Any idea how this can be resolved? Thanks.
I have been using the Nestable JS from Bushell (https://github.com/dbushell/Nestable) for a while and have lots of usable code to generate the menus from Mysql, but I need to drag available options from one list to another list without removing it from the source.
I just want to append it to the list second list. Is this reasonable to do with Nestable or do I need to run back to jquery draggable to get the "clone" option?
Demo I found to illustrate: http://jsfiddle.net/Aub7x/
On this demo it would be easy enough, except when you use $( ".items" ).sortable( "toArray" ); it doesn't return the nested format that I can use to identify parent child relationships. Otherwise it is a much cleaner code base to work off of than the Bushell library.
http://jsfiddle.net/trevordowdle/6CDSB/1/
Since I am linking to JSFiddle, it requires some code to be posted. In the Bushell code I was working hard to find where items were removed from the source, but didn't see it here:
list.el.on('click', 'button', function(e) {
if (list.dragEl) {
return;
}
var target = $(e.currentTarget),
action = target.data('action'),
item = target.parent(list.options.itemNodeName);
if (action === 'collapse') {
list.collapseItem(item);
}
if (action === 'expand') {
list.expandItem(item);
}
});
var onStartEvent = function(e)
{
var handle = $(e.target);
/* callback for beforeDragStart */
list.el.trigger('beforeDragStart', [handle]);
if (!handle.hasClass(list.options.handleClass)) {
if (handle.closest('.' + list.options.noDragClass).length) {
return;
}
handle = handle.closest('.' + list.options.handleClass);
}
if (!handle.length || list.dragEl) {
return;
}
list.isTouch = /^touch/.test(e.type);
if (list.isTouch && e.touches.length !== 1) {
return;
}
e.preventDefault();
list.dragStart(e.touches ? e.touches[0] : e);
/* callback for dragStart */
var item = list.dragEl.find('.'+list.options.itemClass);
list.dragRootEl.trigger('dragStart', [
item, // List item
list.el // Source list
]);
};
I was able to come up with a solution here: https://github.com/dbushell/Nestable/issues/158
You can add the class of "clone" to anything you want to drag from one list to the other.
Put this around line 325:
if ($(dragItem[0]).hasClass("clone"))
{
var cln=dragItem[0].cloneNode(true);
dragItem[0].parentNode.replaceChild(cln, dragItem[0]);
$(dragItem[0]).removeClass("clone")
dragItem.after(this.placeEl);
}else{
dragItem.after(this.placeEl);
dragItem[0].parentNode.removeChild(dragItem[0]);
}
Using Select2 v3.5.2 to create a dynamic option list. The input allows users to select from the list, type to search through options and if the option doesn't exist it is created. The function which created this data is called within an angular controller. (ui-select was not used for this particular input as I did not see how to implement the search + custom input when this was implemented)
var dataQuant = {results: []};
for (var key in response.defaultQuantities) {
var objQuant = response.defaultQuantities[key];
dataQuant.results.push({id:key, text:key + 'otherText'});
}
$('.customClass').select2({
createSearchChoice:function(term, data) {
if ($(data).filter(function() {return this.text.localeCompare(term)===0; }).length===0) {
return {id:term, text:term};
}
},
matcher: function(term, text) {
return text.toUpperCase().indexOf(term.toUpperCase())==0;
},
data: dataQuant,
dropdownCssClass: "customClass2",
selectOnBlur: true,
initSelection : function (element, callback) {
var data = {id: response.defaultQuantity , text: response.defaultQuantity};
callback(data);
}
});
$('.customClass').on('change',function(){
var newQuantityData = $('.customClass').select2('data');
if ($scope.formData["quantity"] != newQuantityData.id){
$scope.formData["quantity"] = newQuantityData.id;
$scope.updateFunction();
}
});
This works perfectly fine in chrome/firefox/opera/safari & IE9 and below. In IE10 and 11 any options seen initially can be clicked and work fine. Any options in the option list hidden initially (user has to scroll to) mousedown jumps back up to the top of the option list. If the mouse is held down and you then scroll back down the options when released the correct option is selected.
After some searching I have found that within the select.js under
// single
postprocessResults: function (data, initial, noHighlightUpdate) {
the culprit was
if (initial === true && selected >= 0) {
this.highlight(selected);
} else {
this.highlight(0);
}
All other browsers have the 'initial' value as true passed into the function. IE10/11 has an object passed in which fails at the if statement resulting in the first option being highlighted. I'm not sure why an object is being passed in rather than true/false which it seems is what it's expecting. Anyone with more understanding of Select2 able to weigh in?
EDIT:
After removing this.highlight(0) I have now found that custom inputs that didn't exist before are not selected, so clicking the enter key does not select them. For now I'm just going to add a conditional to ignore this line if in IE.
I solved this with the following:
var scrollTop;
$('#mySelect2').on("select2:selecting", function( event ){
var $pr = $( '#'+event.params.args.data._resultId ).parent();
scrollTop = $pr.prop('scrollTop');
});
$('#mySelect2').on("select2:select", function( event ){
var $pr = $( '#'+event.params.data._resultId ).parent();
$pr.prop('scrollTop', scrollTop );
});
Perhaps not the most elegant solution, but essentially we listen for the selecting event and then grab the current scrollTop of the selectable options panel. On the actual selection, we reset the panel's scrollTop back to its original. This happens so fast that you should see no jump in the control window. Only test with Select2 v. 4.x.
The nice thing about this solution is that you don't have to hack the component or include anything in your config functions.
Using and seems to function correctly in all browsers.
Changing
if (initial === true && selected >= 0) {
this.highlight(selected);
} else {
this.highlight(0);
}
to
if (initial === true && selected >= 0) {
this.highlight(selected);
} else {
var docMode = document.documentMode,
hasDocumentMode = (docMode !== undefined),
isIE10 = (docMode === 10),
isIE11 = (docMode === 11);
if(hasDocumentMode && (isIE11 || isIE10)){
if(initial.target.value == data.results[0].id || data.results.length == 1){
this.highlight(0);
}
}else{
this.highlight(0);
}
}
Option list no longer jumps to the top in IE 10/11, but still allows users to enter custom values and the 'enter' key selects the typed values.
I have a list of controls contained in a parent div called overlay-controls.
There is many list controls that each have their own overlay-controls.
I am using a for loop to add the event listener to each button that contains the class delete.
Before the user can delete the item, they must confirm. I am trying to attach this to every delete button found in overlay-controls.
I got it to work using a for loop but I know there is a better way using bubbling and capturing. I am having trouble targeting only the delete class inside overlay-controls by bubbling up to parent div.
See the live demo here by clicking on each delete button: http://jsfiddle.net/8qqfeoa2/1/
Here is my code using the for loop:
(function() {
function getConfirmation(e){
var retVal = confirm("Are you sure you want to delete this request?");
if( retVal == true ){
return true;
}else{
e.preventDefault();
return false;
}
}
var del = document.querySelectorAll('.delete');
for(var i = 0, len = del.length; i < len; i++){
del[i].addEventListener('click', function(e) {
getConfirmation(e);
}, false);
}
}());
You dont event need the For / .each loop
Jquery takes care of it internally
$('.delete').on('click', function(e){
getConfirmation(e);
});
Provided you are using jQuery and in getConfirmation method you may also get that specific (clicked) element by using e.target which returns the target on which click happened.
Only Javascript solution
As you requested one
var deletebuttons = document.getElementsByClassName('delete');
for(var button in deletebuttons) {
button.onclick = getConfirmation;
}