JQuery .hasClass for multiple values in an if statement - javascript

I have a simple if statement as such:
if ($('html').hasClass('m320')) {
// do stuff
}
This works as expected. However, I want to add more classes to the if statement to check if any of the classes are present in the <html> tag. I need it so it's not all of them but just the presence of at least one class but it can be more.
My use case is that I have classes (e.g. m320, m768) added for various viewport widths so I only want to execute certain Jquery if it's a specific width (class).
Here is what i have tried so far:
1.
if ($('html').hasClass('m320', 'm768')) {
// do stuff
}
2.
if ($('html').hasClass('m320')) || ($('html').hasClass('m768')) {
// do stuff
}
3.
if ($('html').hasClass(['m320', 'm768'])) {
// do stuff
}
None of these seem to work though. Not sure what I am doing wrong but most likely my syntax or structure.

You could use is() instead of hasClass():
if ($('html').is('.m320, .m768')) { ... }

You just had some messed up parentheses in your 2nd attempt.
var $html = $("html");
if ($html.hasClass('m320') || $html.hasClass('m768')) {
// do stuff
}

For fun, I wrote a little jQuery add-on method that will check for any one of multiple class names:
$.fn.hasAnyClass = function() {
for (var i = 0; i < arguments.length; i++) {
if (this.hasClass(arguments[i])) {
return true;
}
}
return false;
}
Then, in your example, you could use this:
if ($('html').hasAnyClass('m320', 'm768')) {
// do stuff
}
You can pass as many class names as you want.
Here's an enhanced version that also lets you pass multiple class names separated by a space:
$.fn.hasAnyClass = function() {
for (var i = 0; i < arguments.length; i++) {
var classes = arguments[i].split(" ");
for (var j = 0; j < classes.length; j++) {
if (this.hasClass(classes[j])) {
return true;
}
}
}
return false;
}
if ($('html').hasAnyClass('m320 m768')) {
// do stuff
}
Working demo: http://jsfiddle.net/jfriend00/uvtSA/

This may be another solution:
if ($('html').attr('class').match(/m320|m768/)) {
// do stuff
}
according to jsperf.com it's quite fast, too.

For anyone wondering about some of the different performance aspects with all of these different options, I've created a jsperf case here: jsperf
In short, using element.hasClass('class') is the fastest.
Next best bet is using elem.hasClass('classA') || elem.hasClass('classB'). A note on this one: order matters! If the class 'classA' is more likely to be found, list it first! OR condition statements return as soon as one of them is met.
The worst performance by far was using element.is('.class').
Also listed in the jsperf is CyberMonk's function, and Kolja's solution.

Here is a slight variation on answer offered by jfriend00:
$.fn.hasAnyClass = function() {
var classes = arguments[0].split(" ");
for (var i = 0; i < classes.length; i++) {
if (this.hasClass(classes[i])) {
return true;
}
}
return false;
}
Allows use of same syntax as .addClass() and .removeClass(). e.g., .hasAnyClass('m320 m768')
Needs bulletproofing, of course, as it assumes at least one argument.

var classes = $('html')[0].className;
if (classes.indexOf('m320') != -1 || classes.indexOf('m768') != -1) {
//do something
}

The hasClass method will accept an array of class names as an argument, you can do something like this:
$(document).ready(function() {
function filterFilesList() {
var rows = $('.file-row');
var checked = $("#filterControls :checkbox:checked");
if (checked.length) {
var criteriaCollection = [];
checked.each(function() {
criteriaCollection.push($(this).val());
});
rows.each(function() {
var row = $(this);
var rowMatch = row.hasClass(criteriaCollection);
if (rowMatch) {
row.show();
} else {
row.hide(200);
}
});
} else {
rows.each(function() {
$(this).show();
});
}
}
$("#filterControls :checkbox").click(filterFilesList);
filterFilesList();
});

This is in case you need both classes present. For either or logic just use ||
$('el').hasClass('first-class') || $('el').hasClass('second-class')
Feel free to optimize as needed

Try this:
if ($('html').hasClass('class1 class2')) {
// do stuff
}

Related

Get element from this array $scope.pricingList= [[Object { amount="1", type="HOURLY"}], [Object { amount="1", type="HOURLY"}]]

So far I have tried this:
if ($scope.flag) {
$scope.partialPricing.push([$scope.p]);
$scope.flag = false;
} else {
for (var i = 0; i < $scope.partialPricing.length; i++) {
console.log("finding object = " + $scope.partialPricing);
if ($scope.partialPricing[i].type != $scope.p.type) {
$scope.partialPricing.push([$scope.p]);
break;
} else {
console.log("Already Given . Please Clear .");
}
}
}
Problem is when it enters into the else condition, it gets
$scope.partialPricing = [Object Object] and also, $scope.partialPricing[i].type = undefined.
My goal is to prevent user from giving same type twice. Here type is Hourly , Monthly , Weekly.
He can set the value only once. Tell me the solution or any other way i can do it?
Your if condition in the loop won't work properly: it will add the element to the list if its type is different from the first element of the list, independently of all the rest of the list.
The easiest is to make a lookup function as follows:
function lookupByType(type) {
for (var i = 0; i < $scope.partialPricing.length; i++) {
if ($scope.partialPricing[i].type == type) {
return true;
}
}
return false;
}
And then use it as follows (no for loop here):
if (lookupByType($scope.p.type)) {
console.log("Already Given . Please Clear .");
} else {
$scope.partialPricing.push($scope.p);
}
About $scope.flag, I assume you're aware that you use it to bypass the verification, you probably have a good reason for this. However, if the goal is only to insert the first element, there's no need for it: the lookup function will always return false if the list so far is empty.
Edit: You also had a type problem: your pricing list was an array of array of objects, and you used it as an array of objects. You probably want to use the latter, so you need to push($scope.p) rather than push([$scope.p]).
You may switch your else part a bit and check for equality, because that is what you need for exiting the loop. Then break and make your next action according of the check.
if ($scope.flag) {
$scope.partialPricing.push([$scope.p]);
$scope.flag = false;
} else {
var found = false;
for (var i = 0; i < $scope.partialPricing.length; i++) {
console.log("finding object = " + $scope.partialPricing);
if ($scope.partialPricing[i].type === $scope.p.type) {
console.log("Already Given . Please Clear .");
found = true;
break;
}
}
if (!found) {
$scope.partialPricing.push([$scope.p]);
}
}
Otherwise, you could use Array#some and perform a check
if ($scope.flag) {
$scope.partialPricing.push([$scope.p]);
$scope.flag = false;
} else {
var found = false;
if ($scope.partialPricing.some(function (price) { return price.type === $scope.p.type; })) {
console.log("Already Given . Please Clear .");
} else {
$scope.partialPricing.push([$scope.p]);
}
}
Why you need to push "$scope.P" into the partialPricing. Any logic related you handled this line?
$scope.partialPricing.push([$scope.p]);

One function that returns an array, or multiple functions?

I came across this in some code I am reworking and am curious if there is any reason for doing it this way besides personal preference. It seems unnecessarily obfuscated to me, considering how easy it would be to use several small functions with descriptive names. The purpose of the code is to validate a number of variables to ensure the data is properly formatted and within acceptable ranges while generating business data reports. The report is mainly just a tool to bring attention to scheduling issues.
There is a single function that gets passed several values, runs a test on each one, then passes all the results back as a boolean array.
function testAll(test1, ..., test10) {
var results = [false, ..., false];
if (test1 condition == true) {
results[0] = true;
}
...
if (test10 condition == true) {
results[9] = true;
}
return results;
}
That function is then called and used like this.
var tblData = getCurrentData(); // function that gets database info through AJAX
for (i = 0; i < tblData.Rows.Count; i++) {
// some code to append table element
var results = testAll(strStartDate, ..., strTotalHours);
if (results[0] == true) {
$('#startDate' + i.toString()).css('background-color', 'red');
}
...
if (results[9] == true) {
$('#projectTime' + i.toString()).css('background-color', 'red');
}
}
The original writer is gone and did not comment his code
While this may not technically address the question I posed, it is the solution to cleaning up the code in this case.
After further review it made more sense to use a non value returning function that performs the css formatting because of how simple the the tasks after evaluation are.
-Modified function
function verifyData(startDate, ..., timeSpent) {
if (startDate isValid != true) {
$('#startDate' + i.toString()).css('background-color', 'red');
}
...
if (timeSpent isValid != true) {
$('#projectTime' + i.toString()).css('background-color', 'red');
}
}
-New method of calling and using
var tblData = getCurrentData(); // function that gets database info through AJAX
for (i = 0; i < tblData.Rows.Count; i++) {
// some code to append table element
verifyData(strStartDate, ..., strTotalHours, i);
}

Run function when inputs are not empty

Let's start with the fiddle: http://jsfiddle.net/Gytrn/
Here I am mixing angular and jquery like a bad person, I know, but the underlying question is this:
How can I more elegantly execute the javascript to fade in the paragraph once all the inputs are filled in?
Currently the code is as such:
$('input').focusout(function(){
var allInput = $('input')
var inArr = []
for (var i = 0; i < allInput.length; i++) {
inArr.push($('input:eq('+i+')').val().length)
}
console.log(inArr);
if(inArr.sort()[0]===0){
} else {
$('p').fadeIn(750)
}
});
It feels very heavy handed and I would like to know a better way. Feel free to ignore the fact that I'm using Angular and that there is an Angular way to do this. I would like to know a vanilla js or js and jquery solution to this problem that is more elegant.
Proposed improvements:
1) Inputs cached once, not every time your handler fires
2) focusout handler returns when first empty field found
3) Quick native check of input for emptiness
$(function () {
var allInput = $('input');
$('input').focusout(function () {
for (var i = 0; i < allInput.length; i++) {
if (allInput[i].value.length === 0) {
return;
}
}
$('p').fadeIn(750);
});
});
JSFiddle
There are a bunch of ways to do it
var inputs = $("input");
inputs.on("change", function () {
var allFilled = true;
inputs.each(
function(){
if(this.value.length===0){
allFilled = false;
return false; //break loop
}
}
);
$("#myParagraph").toggle(allFilled);
});

jQuery - add several textboxes

Im using this code:
jQuery.fn.update_textarea = function(test) {
$("#articles_textarea").html('');
for (i=0;i<test;++i) {
if (message[i]) { $("#articles_textarea").html('<textarea></textarea>'); }
else { message[i] = ''; $("#articles_textarea").html('<textarea></textarea>'); }
}
}
When im trying to add more then one, nothing happends but if i add 1 it works just as it should..
This is the "call"code
$("#articles_textarea").update_textarea(total);
This is variable total:
var total = parseFloat($(".testCounter").val()) + 5;
This is for calling only one textarea (the part that works):
$("#articles_textarea").update_textarea(1);
When i call one box it's working when i call several boxes nothing happends at all..
try with append method. .html() will remove the previous content
jQuery.fn.update_textarea = function(test) {
$("#articles_textarea").html('');
for (i=0;i<test;++i) {
if (message[i]) { $("#articles_textarea").append('<textarea></textarea>'); }
else { message[i] = ''; $("#articles_textarea").append('<textarea></textarea>'); }
}
}
Why you use jQuery.fn, when you don't use its functionality, whereas it works, only for the textareas with articles_textarea Id, you better do it using a simple function like:
function update_textarea(test) {
$("#articles_textarea").html('');
for (var i=0;i<test;++i) {
if (!message[i]){
message[i] = '';
}
$("#articles_textarea").append('<textarea></textarea>');
}
};
adding a new function to jQuery.fn is used to create new jQuery plugins, check this link for more info.
the other point was not using var i in your for loop which is not a good practice. Moreover both your if statement and the else, were the same, except message[i] = ''; part.
BTW if you want to do it using jQuery.fn, you better do it like:
jQuery.fn.update_textarea = function(test) {
this.html('');
for (var i=0;i<test;++i) {
if (!message[i]){
message[i] = '';
}
this.append('<textarea></textarea>');
}
};

DOM help, parents of parents, children of children

I am using Drupal's Webform module which writes all the ids and classes automatically, so I can't add an Id to the place I want to add it. And Drupal doesn't put one in for me. Grrr.
I have no problem when I use my code with just a simple getElementById('myid'), but the element I need is a blank, no id, no class "a" that's inside a "legend" (which has a class but no id) that's inside a fieldset with an id.
I tried this code and several variations but it didn't work:
document.getElementById('webform-created-id-here').getElementsByTagName('legend').getElementsByTagName('a');
I feel like I'm not understanding how to properly access that part of the DOM. Can anyone offer help or suggestions?
Thank you!
getElementsByTagName return a nodelist with the elements found, so you must select the index of the element
Example for the first element
var test = document.getElementById('webform-created-id-here').getElementsByTagName('legend')[0].getElementsByTagName('a')[0];
DEMO VIEW
Recurse through the DOM yourself. References to the children of any element are stored in the childNodes attribute which exist for every node.
function recurseDOM (el,test) {
if (test(el)) {
return el;
}
else {
var children = el.childNodes;
var result;
for (var i=0; i<children.length; i+) {
result = recurseDOM(children[i],test);
if (result) {
return result;
}
}
}
return null;
}
This is only one possible implementation that I wrote in the 2 minutes it took me to type out this answer. So it can probably use some improvements. But you get the idea.
Use it like this:
recurseDOM(document.body,function(el){
if (el == something_something) { return 1}
return 0
});
You can write a similar function to test parents:
function testParents (el, test) {
var parent = el.parentNode;
if (test(parent)) {
return 1;
}
else {
if (parent != document.body) {
return testParents(parent,test);
}
}
return 0;
}
So you can write something like:
recurseDOM(document.body,function(el){
if (el.tagName == 'a' && testParents(el,function(p){
if (p.tagName == 'legend' && testParents(p,function(pp){
if (pp.id == 'webform-created-id-here') {
return 1;
}
return 0;
})) {
return 1;
}
return 0;
})) {
return 1;
}
return 0;
});
Alternatively, you can start the recursion form the getElementById instead of document.body.

Categories