Select2 issue with adding class to Select2 container in WooCommerce instantiated instance - javascript

Ok, I have a WordPress/WooCommerce site and I am using Select2 for my select fields, but it seems WooCommerce also uses it for theirs.
I instantiate mine as such:
$('select').not('[multiple]').select2({
minimumResultsForSearch: Infinity,
dropdownAutoWidth : true,
});
Then I also want to apply a different border colour to fields that already have a value set, so I added this code:
$('select').each(function(e) {
handleSelectSelection($(this));
});
function handleSelectSelections(select) {
var el = (select.next('.select2').length) ? jQuery(select.data('select2').$container) : select;
if (select.val() !== "" && select.val() !== null) {
el.addClass('has-selection');
} else {
el.removeClass('has-selection');
}
}
Please note that all code outside the function is inside a (function($) { wrapper.
Now, this works fine with the select2 fields my code instantiates, however it doesn't seem to work with the ones WooCommerce instantiates.
I have checked the differences in the select fields and I noticed my ones have a data-select2-id attribute, whilst the WooCommerce ones do not.
I did some console logging with jQuery(select.data('select2').$container) on the WooCommerce ones and it appears quite normal however.
Testing the values returns correct and it even goes inside the section where it attempts to add the class, but it just fails to do so.
Edit: The interesting thing is it works if you bind it to a change event, but not this way.
Any ideas on what is happening here?

Related

DataTables column.visible hides the first column despite returning true

I'm using Data Tables and jQuery right now. I have a simple table that has settings that are toggled via checkbox. When the setting is clicked (checked), I get the elements attr('data-column') and use that to either show or hide that table column based on what the user wants.
This works when the user selects a setting by using the following code:
// User clicks on a checkbox to toggle a setting on/off
$('.setting').click(function(){
let table = $('#my-table');
const current_element = $('input[name="test"][type="checkbox"]');
const data_column_number = current_element.attr('data-column');
if(current_element.is(":checked")){
current_element.prop('checked',false);
table.column(data_column_number).visible(false);
}
else {
current_element.prop('checked',true);
table.column(data_column_number).visible(true);
}
});
Now, I have the user's pre-saved settings in a JSON array. I iterate through the array, and if a setting is checked I make the column number visible (visible(true)). If the setting should not be checked I set it's visible attribute to false (visible(false)). It looks like this:
let table = $('#my-table');
// assume value has an index called ['checked'] that is empty or set to 'checked'
$.each(settings,function(key,value){
let current_element = $('input[name="'+key+'"][type="checkbox"]');
let data_column_number = current_element.attr('data-column');
if(value['checked'] === 'checked'){
current_element.prop('checked',true);
if(table.column(data_column_number).visible() === false){
table.column(data_column_number).visible(true);
}
}
else {
current_element.prop('checked',false);
if(table.column(data_column_number).visible() === true){
table.column(data_column_number).visible(false);
}
}
});
For whatever reason, the code block above works except for the first data-column. I've used console.log to print out each data-column and to see what the values before and after table.column(data_column_number).visible() are and they are correct.
So if I check "test", and then click something to load settings where "test" is enabled within those settings, it will hide "test" despite console.log showing that the data-column is 0 (correct), that visible() is set to true before any change (correct) and that visible() is still set to true by the end of the $.each() loop. Despite this, my first column disappears.
If my settings hide another column that is not at position 0, it works though.
Does anyone see what could be causing this?
I found the answer. This might not apply to everyone, but just in case, make sure that the attribute data-column is not undefined. What was happening in my case was that somewhere down the line I had sub-items in the table, so some headings had no data-column attribute.
The way data tables works (at least in my case) was that setting an undefined column's visible status to either true or false, in essence, calling table.column(undefined).visible(false) or table.column(undefined).visible(true) would set table.column(0).visible(false), no matter what.
Just adding a check to see if data_column_number !== undefined before proceeding with setting a visible state eliminated my issue.

Item is moving on mouse click in bootstrap-duallistbox

I have taken the customized plugin which gives the feature for optgroup which is given over here Add scroll position return on select1. Add optgroup capability
Customized bootstrap-duallistbox (with optgroup feature)
I have created a sample example in plunker which shows a running example. The option group is working fine but the issue is that even through when I put move-on-select="false" still Item is moving on mouse click.
Can anyone please tell me why this is behaving like that
Working Plunker
<select ng-model="modal.selectedItems"
ng-options="item.name group by item.app for item in modal.allItems"
multiple
bs-duallistbox
move-on-select=false
></select>
Honestly, the easiest solution is just to change the implementation of selectGroup. I think it should be this:
function selectGroup(e) {
if(e.data.dlb.settings.moveOnSelect) {
move(e.data.dlb);
}
e.preventDefault();
}
You'll probably want to make a similar change to unselectGroup. The current implementation has strange behavior, where it moves things that aren't selected since it never unselects anything properly.
Edit:
The way that selections are made is faulty. I have no idea of the author's intent, but I suspect this implementation is closer to what a user would expect:
function selectGroup(e) {
if (e.target.tagName != 'OPTGROUP') return;
$(this).find('option').each(function (index, item) {
var $item = $(item);
if (!$item.data('filtered1')) {
if (!$item.prop('selected')) {
$item.prop('selected', true);
} else {
$item.removeAttr('selected');
}
}
});
if(e.data.dlb.settings.moveOnSelect) {
move(e.data.dlb);
}
e.preventDefault();
}
Again, make a similar change to unselectGroup. In the original code, the problem was that when you click on an individual option, the click would bubble up to the optgroup, hence the if guard. Also, the selection state should not be changed directly. That is already handled in the move function. It's much nicer to change the selected attribute, which the the move function later digests. In this way, it's also visually clear what is actually being selected. Thus, when you click an optgroup, it should toggle selected on each the item properties. You may want to modify how the removal of selected attribute is done.
Go with
http://www.virtuosoft.eu/code/bootstrap-duallistbox/
as #prakash Said in Commments.

Jquery DataTable keeps going back to first page when changing the checked status of checkboxes in other pages with javascript

I have the following code
$('input[name="select-all"]').click(function() {
//channel-data is initialised as DataTable
$('#channel-table').find('input[name="batch-select"]:checked').each(function () {
$(this).prop("checked",true);
});
});
whenever I click the select all button in any page other than 1st The datatable keeps reverting back to the first page.
I think it happens because of redraw due to change in checkboxes property.Any work around this?
Yes, probably - but have not seen any reason for test your hypothesis :) When dealing with dataTables, always go through the API! There is a neat every() method you can use to cycle through all rows and update a checkbox as checked. Here is an example, have tried to reproduce your setup with a "select all" button and checkboxes :
$('input[name="select-all"]').click(function() {
table.rows().every( function ( rowIdx, tableLoop, rowLoop ) {
var $tr = this.nodes().to$()
$tr.find('input[name="batch-select"]').prop('checked', true)
})
})
demo -> http://jsfiddle.net/u9Lq56v1/
does not change page
does actually update checkboxes also on hidden pages (that would the code in OP fail as well)

jQuery Validate, Select2, and Bootstrap 3 Popovers - How to Bind Popover To Select2's Parent Elements Instead of Hidden Select Element

I've implemented the excellent method to use Bootstrap 3's Popovers to show validation error messages, and for the most part it works well - except when it comes to hidden or replaced elements, like Select2 or CKEditor. The popover positions itself correctly on regular elements like input and normal selects, but not a select enhanced by Select 2.
I've created a Fiddle that Sparky fixed so it actually works: http://jsfiddle.net/jemxtthb/4/
$("#noticeSentTo").select2();
var validator = $("#documentAdmin").validate({
debug: true,
ignore: "",
showErrors: function(errorMap, errorList) {
$.each(this.successList, function(index, value) {
return $(value).popover("hide").parents(".form-group").removeClass('has-error').addClass('has-success');
});
return $.each(errorList, function(index, value) {
var _popover;
_popover = $(value.element).popover({
trigger: "manual",
placement: "auto top",
content: value.message,
container: "body",
template: "<div class=\"popover\" role=\"tooltip\"><div class=\"arrow\"></div><div class=\"popover-content\"><p></p></div></div>"
});
_popover.data("bs.popover").options.content = value.message;
return $(value.element).popover("show").parents(".form-group").addClass('has-error').removeClass('has-success');
});
}
});
});
Here's a screen capture of what I see within my application:
I've tried using the selector option on the popovers, but can't figure out how to specify the current element the popover's being applied to (if that's even the right way to do it).
As you can see from my screen capture, the popover appears up and to the far left of the element (in red) it's supposed to be bound to because Select2 is hiding the original select.
UPDATE: Based on #Sparky's suggestion, I've changed it to use errorPlacement instead of the method I used previously - the only issue now is getting the condition where if it's a hidden element (like Select2 or CKEditor) to provide the Popup to the parent object instead of the element itself (since Popovers cannot work on hidden elements, apparently) - I've put in some debug console.log code just to make sure the if statement in the jQuery is firing correctly:
var validator = $("#documentAdmin").validate({
ignore: [],
errorPlacement: function (error, element) {
var lastError = $(element).data('lastError'),
newError = $(error).text();
$(element).data('lastError', newError);
if (newError !== '' && newError !== lastError) {
$(element).popover({
trigger: "manual",
placement: "auto top",
content: newError,
container: "body",
template: "<div class=\"popover\" role=\"tooltip\"><div class=\"arrow\"></div><div class=\"popover-content\"><p></p></div></div>"
});
if (element.is(':hidden')) {
// $(element).next().parents(".form-group").addClass('has-error').removeClass('has-success').popover("show");
$(element).next('span').popover('show').addClass('has-error').removeClass('has-success');
console.log('hidden element');
} else {
$(element).popover("show").parents(".form-group").addClass('has-error').removeClass('has-success');
console.log('normal element');
}
}
},
success: function (label, element) {
$(element).popover("hide").parents(".form-group").removeClass('has-error').addClass('has-success');
}
});
And here's an updated Fiddle: http://jsfiddle.net/jemxtthb/6/
OK - really odd - the Fiddle above actually works as expected! Now I need to figure out why it works in the Fiddle and not my app...
Any help/guidance is appreciated!
Looking at the errors in the JavaScript console and fixing the following in your jsFiddle...
There is no point in having a select element with validation if it comes pre-loaded with a value. In other words, there is nothing to validate if every option already contains a value or there is an option with a value that has the selected attribute. Typically, the first option contains value="".
<option value="">Please select<option>
There was a 404 error on your Bootstrap script file. The CDN URL was mangled.
Not critical, but it should be ignore: [], not ignore: "".
You're getting a "no 'name' assigned" error in the debug console because the hidden input element created by Select2 does not contain a name attribute. It's apparently not an issue now, but it could be an issue when you have more elements on the page. The jQuery Validate plugin mandates that all elements to be validated contain a name attribute.
DEMO: http://jsfiddle.net/jemxtthb/4/
for the most part it works well - except when it comes to hidden or replaced elements, like Select2 or CKEditor. The popover positions itself correctly on regular elements like input and normal selects, but not a select enhanced by Select 2.
You should not use showErrors for tooltips because this callback function is typically used for generating a list of all messages, like for a summary at the top of the form. "Gets the map of errors as the first argument and an array of errors as the second."
Instead, you'll need to integrate your tooltips plugin with jQuery Validate by using its errorPlacement and success callback functions; which will put the single pending error message inside a single tooltip. This integration also depends on the available options of your popovers plugin... like can you dynamically change the text inside a tooltip? Can you dynamically/programmatically show/hide them, etc?
Then within errorPlacement you can conditionally target certain elements to tweak the placement when the element is hidden by Select2, etc.
Something more like this...
$("#documentAdmin").validate({
ignore: [],
errorPlacement: function (error, element) {
// put text of error into tooltip with $(error).text()
// conditional placement of the tooltip
// show the tooltip
},
success: function (label, element) {
// hide the tooltip
},
// any other options
});
I'm using the ToolTipster jQuery plugin like this.
As it turns out, the above code worked correctly from the beginning - after #Sparky fixed my Fiddle, the popover worked correctly, positioning itself in relation to the Select2's parent element.
What benefit came out of this was #Sparky's suggestion to use errorPlacement versus the method I was originally using, which gives more control over individual elements' error message placement methods.
I am currently investigating (and suspect) the culprit is an offcanvas plugin I'm using for a sidebar - I didn't include this in the Fiddle because I wanted to keep the Fiddle as concise as possible to the issue and not complicate things. I'll update this when I find out what the problem is. If anyone's interested in implementing this method for validation errors in Bootstrap 3 Popovers, I would use the code in the second Fiddle I posted.

jQuery Custom Validators

I'm using the jQuery validation plugin and I'm looking to add some custom logic. I have a series of checkboxes which have children checkboxes associated with them. For certain (not all) of these parent checkboxes, I want to require that one of the children checkboxes is checked. I have no issue hardcoding for this so I was adding a field like this to my DOM:
<input type="hidden" id="child_required_1" class="child_required_1" />
And then adding a custom validator like this:
jQuery.validator.addMethod('child_required_1', function(val, element) {
if($('#product_responses_1').length > 0) {
if($('#product_responses_1').is(':checked')) {
var count = $("input:checkbox:checked[id^='children_tags_1_']").length;
if(count == 0) {
return false;
}
}
}
return true;
}, 'You must select at least one child.');
This works perfectly fine. But when I duplicate all of this and add "_2", only one of the validators seems to fire. So from what I can gather, custom validators are unique per form? If that's the case, how am I supposed to handle a situation like this where I may need 15-20 of these all showing in different places? I don't want to just show one error.
I could also create a class rule but that doesn't solve my problem of creating multiple error labels and placing them in the relevant positions.
Apparently having it on a hidden field didn't work, but when I removed the hidden fields and applied that class to the actual checkboxes themselves, it worked fine. Not entirely sure why.

Categories

Resources