Having trouble working with SelectWoo instances of Select2 within WooCommerce - javascript

I am using Select2 within WooCommerce in some of my own custom areas and I am targeting it with some code to add and removes certain classes and it's working fine except for the SelectWoo instances used by WooCommerce themselves do not add the class using el.addClass('has-selection'); in the example provided.
Example code:
(function($) {
$('select').each(function(e) {
handleSelectSelections($(this));
});
})( jQuery );
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');
}
}
Everything works fine, except when it gets to the actual part where it adds the class it doesn't work - no class is added.
Am I missing something here?

May be
if (select.val() !== "" && select.val() !== null) {
**el.className += 'has-selection';**
} else {
el.removeClass('has-selection');
}

Related

converting javascript to jquery function not correctly working

I am trying to convert a small script from javascript to jquery, but I don't know where I should be putting the [i] in jquery?. I am nearly there, I just need someone to point out where I have gone wrong.
This script expands a search input when focused, if the input contains any values, it retains it's expanded state, or else if the entry is removed and clicks elsewhere, it will snap back.
Here is the javascript:
const searchInput = document.querySelectorAll('.search');
for (i = 0; i < searchInput.length; ++i) {
searchInput[i].addEventListener("change", function() {
if(this.value == '') {
this.classList.remove('not-empty')
} else {
this.classList.add('not-empty')
}
});
}
and converting to jquery:
var $searchInput = $(".search");
for (i = 0; i < $searchInput.length; ++i) {
$searchInput.on("change", function () {
if ($(this).value == "") {
$(this).removeClass("not-empty");
} else {
$(this).addClass("not-empty");
}
});
}
Note the key benefit of jQuery that it works on collections of elements: methods such as .on automatically loop over the collection, so you don't need any more than this:
$('.search').on("change", function() {
this.classList.toggle('not-empty', this.value != "");
});
This adds a change event listener for each of the .search elements. I've used classList.toggle as it accepts a second argument telling it whether to add or remove the class, so the if statement isn't needed either.

jQuery Input Colour Changes Not functioning correctly

I'm trying to get my input fields (associated by class) to change colour when they have content.
This is working, but removing the class isn't activating properly, and I'm unsure why. If I enter content into the first, then second input, delete the content from the first, the class is removed, however if I delete the content from the second before the first, the background remains blue.
I can't use the textboxes Id's as they're already in use.
JS Code:
$('.checkFiller').on('change', function () {
if ($('.checkFiller').length > 0) {
$(this).addClass('allFiller');
}
if ($('.checkFiller').val() == '' || $('.checkFiller').val() == 0 || $('.checkFiller').val() == null) {
$(this).removeClass('allFiller');
}
});
Here is a jsfiddle to demo my frustration - what am I doing wrong?
https://jsfiddle.net/qwgj77tm/1/
You need to use this to get the one you are editing use .hasClass() and .length to acheive this
$('.checkFiller').on('change', function () {
if($(this).val().length==0){
$(this).removeClass('allFiller');
}else $(this).addClass('allFiller');
});
.allFiller {
background-color: #333366;
color: #ffffff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input class="checkFiller">
<input class="checkFiller">
<input class="checkFiller">
You should change your references from $('.checkFiller') to $(this) in the on:
$('.checkFiller').on('change', function () {
if ($(this).length > 0) {
$(this).addClass('allFiller');
}
if ($(this).val() == '' || $(this).val() == 0 || $(this).val() == null) {
$(this).removeClass('allFiller');
}
});
As is, you're checking the first input with the class, rather the one that has changed.

Select2 3.5.2 mousedown jumps to top of list in IE10 & IE11

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.

if this input has value doesn't work in IE 9

I am using this simple code to filter through a search form with many text inputs and see if they have a value and then add a class.
Works perfectly in Chrome, safari and Firefox but not in IE9.
$('input[type="text"]').filter(function() {
if ($(this).val() !== '') {
$(this).addClass('used');
}
});
Please advice, thanks in advance!
EDIT
Change to each but doesn't solve the issue... Here it is with the event that triggers the function...
$(document).on('event-ajax-form-is-loaded', function() {
$('input[type="text"]').each(function() {
if ($(this).val() !== '') {
$(this).addClass('used');
}
});
});
From the limited information you shared, this is how you should be doing this:
$('input[type="text"]').filter(function() {
return $(this).val() !== '';
}).addClass('used');
.filter() is supposed to reduce a set of matched elements so its filter function should always return a bool instead of manipulating the DOM.
Edit: Based on your updated code snippet and the page link you shared in the comments, if you are using jQuery in WordPress, then its always safer to wrap the code like so:
(function($) {
/* jQuery Code using $ object */
})(jQuery);
enter code hereIn JS you can check the element value by getting their tag name
for (var i = 0; i < document.getElementsByTagName('input').length; i++){
if (document.getElementsByTagName('input')[i].value == "")
{
alert("The value of textbox at " + i + " is empty");
}
}
Working Demo
Or like what other people suggest, use a .each in JQuery
$('input[type="text"]').each(function(i){
if ($(this).val() == "") {
alert("The value of textbox at " + i + " is empty");
}
});
anohter Working Demo
If you insist to use filter and here you go
$('input[type="text"]').filter(function()
{ return $( this ).val() != ""; }).addClass("used");
Last Working Demo
and jquery filter reference

Jquery plugin with prototype

I was trying on the basics of Jquery plugin and the prototype concept, but ended up in an unusual behavior.
HTML :
<div>
<span>
<textarea>Text Area with 500 characters. Adding Some text.</textarea>
<span class="cl"></span>
</span>
<span>
<textarea>Text Area with 100 characters</textarea>
<span class="cl"></span>
</span>
</div>
JQuery :
(function ($) {
var tisCharsLeftCntxt = null;
function fnCharsLeft(ele, genStngs) {
this.jqe = $(ele);
this.maxChars = genStngs.maxChars;
tisCharsLeftCntxt = this;
this.fnInit();
}
fnCharsLeft.prototype = {
fnInit: function () {
tisCharsLeftCntxt.fnUpdateRemainingChars();
tisCharsLeftCntxt.jqe.keyup(function (event) {
key = event.keyCode ? event.keyCode : event.which;
if ((37 != key) && (38 != key) && (39 != key) && (40 != key)) {
tisCharsLeftCntxt.fnUpdateRemainingChars();
}
});
},
fnUpdateRemainingChars: function () {
var charsLft = tisCharsLeftCntxt.maxChars - tisCharsLeftCntxt.jqe.val().length,
jqeDestToUpdt = tisCharsLeftCntxt.jqe.siblings('.cl');
charsLft = (charsLft < 0) ? 0 : charsLft;
if (charsLft) {
jqeDestToUpdt.text(charsLft + ' more of ' + tisCharsLeftCntxt.maxChars + ' characters');
} else {
tisCharsLeftCntxt.jqe.val(tisCharsLeftCntxt.jqe.val()
.substring(0, tisCharsLeftCntxt.maxChars));
tisCharsLeftCntxt.jqe.scrollTop(tisCharsLeftCntxt.jqe[0].scrollHeight);
jqeDestToUpdt.text("Maximum limit of " + tisCharsLeftCntxt.maxChars + " characters reached");
return false;
}
}
};
$.fn.fnCharsLeftPlgn = function (genStngs) {
return $(this).data("charsleft", new fnCharsLeft(this, genStngs));
};
})(window.jQuery);
$('div span:nth-child(1) textarea').fnCharsLeftPlgn({maxChars: 500});
$('div span:nth-child(2) textarea').fnCharsLeftPlgn({maxChars: 100});
Fiddle :
http://jsfiddle.net/5UQ4D/ & http://jsfiddle.net/5UQ4D/1/
Requirement is, the plugin should show the number of characters that can be added in a text-area. If there is only one text-area in a page this is working good. But if there are more than one, only the text-area which is last associated with the plugin is working properly.
With respect to code here, In both the text-area number of characters left is updated correctly during initialization (only for the first time). But later when the text area content is changed, only the second with 100 chars (or the most recent text-area associated with the plugin) is working properly.
Seems like, I'm failing to restrict the plugin context independently to a text-area. Please Advice,..
Problem 1:
As mentioned in the comments, you're creating a variable named tisCharsLeftCntxt outside of the other contexts, then assigning this to it in your constructor. Every time you run your plugin you stomp on tisCharsLeftCntxt with a new this.
There is no reason to use a reference to this in the wholesale fashion in which you have. There is only one place in your code where the scope changes such that this is no longer your instance. That place is inside of the keyup event handling function. You should localize your aliasing of this to just the method which contains that event handler.
Problem 2:
I believe another part of your problem (this would be seen if you ran the plugin against a selector which matched more than one element) is inside of the plugin function (the one which lives off of $.fn).
$.fn.fnCharsLeftPlgn = function (genStngs) {
return $(this).data("charsleft", new fnCharsLeft(this, genStngs));
};
It should be:
$.fn.fnCharsLeftPlgn = function (genStngs) {
return this.each(function () {
$(this).data("charsleft", new fnCharsLeft(this, genStngs));
});
};
When directly inside of a method which has been added to the jQuery prototype ($.fn), this refers to the entirety of the current collection, not an element. A plugin should each itself in order to run element specific logic against its individual members.
Without using .each() you are calling .data() against an entire collection, setting all of their charsleft data properties to the one instance of fnCharsLeft. By using .each() you create a new instance of fnCharsLeft for each of the elements in the collection.
Since the .each() then returns the collection, and a plugin should be chainable, you simply return it.
A rule of thumb is that if you're passing this into the jQuery factory ($()) directly inside of a plugin, function then you're doing something wrong since it is already the collection. As a second rule of thumb, almost all plugin definitions except those which are intended to return info about an element (such as .val(), .html(), or .text() when not given a param) should start with return this.each(function() {...
Solutions:
Bringing those changes together results in this fiddle: http://jsfiddle.net/5UQ4D/4/
And this code:
(function ($) {
var fnCharsLeft = function (ele, genStngs) {
this.jqe = $(ele);
this.maxChars = genStngs.maxChars;
this.fnInit();
};
fnCharsLeft.prototype = {
fnInit: function () {
var instance = this;
this.fnUpdateRemainingChars();
this.jqe.on('keyup', function (e) {
key = e.keyCode ? e.keyCode : e.which;
if (37 != key && 38 != key && 39 != key && 40 != key) {
instance.fnUpdateRemainingChars();
}
});
},
fnUpdateRemainingChars: function () {
var charsLft = this.maxChars - this.jqe.val().length,
jqeDestToUpdt = this.jqe.siblings('.cl');
charsLft = charsLft < 0 ? 0 : charsLft;
if (charsLft) {
jqeDestToUpdt.text(charsLft + ' more of ' + this.maxChars + ' characters');
} else {
this.jqe
.val(this.jqe.val().substring(0, this.maxChars))
.scrollTop(this.jqe[0].scrollHeight);
jqeDestToUpdt.text("Maximum limit of " + this.maxChars + " characters reached");
return false;
}
}
};
$.fn.fnCharsLeftPlgn = function (genStngs) {
return this.each(function () {
$(this).data('charsleft', new fnCharsLeft(this, genStngs));
});
};
}(window.jQuery));

Categories