In an each function, I search through the DOM for many elements with a specific className. This ClassName, according to its length, will create an array that is long 2 or 4.
I need to select these two type of element separately in order to act differently.
How can I do that?
<div class="foo-1"></div>
<div class="foo-1"></div>
<div class="foo-1-boo-2"></div>
jQuery
$( '[class*=foo-]' ).each(function() {
var foo = $( this );
var fooArray = foo.attr( 'class' ).split( '-' );
var arrayLength = fooArray.length;
});
the console will return that there are 3 elements, two of them have length 2 and one 4.
I need to separate these two results in order to act differently as they were variables.
I need something like:
var caseA = (foo with an arrayLength === 2);
var caseB = (foo with an awwayLength === 4);
One possible way is to use .filter method with a function as an argument:
var elements = $('[class*=foo-]'),
caseA = elements.filter(function() {
return this.className.split('-').length === 2;
}),
caseB = elements.filter(function() {
return this.className.split('-').length === 4;
});
console.log(caseA.length, caseB.length);
I'm guessing you want these as jQuery sets instead of arrays so you can easily manipulate them en masse with jQuery. So this will do it:
var caseA = $('[class*=foo-]').filter(function() {
return $(this).attr("class").split("-").length === 2;
});
var caseB = $('[class*=foo-]').filter(function() {
return $(this).attr("class").split("-").length === 4;
});
If you have tons of elements and it proves to be slow you can optimize this slightly by making it more complex and using one each instead of filter. But I wouldn't bother until it proves to be necessary.
Another possible way is to just use a conditional statement -
$('[class*=foo-]').each(function () {
var foo = $(this);
var fooArray = foo.attr('class').split('-');
var arrayLength = fooArray.length;
(2 == arrayLength ? console.log('to do something') : '');
(4 == arrayLength ? console.log('do something for') : '');
});
I mixed two solutions in the comment of this post:
var caseA = col.filter(function() {
return this.className.split( '-' ).length === 2;
});
var caseB = col.filter(function() {
return this.className.split( '-' ).length === 4;
});
Related
I got to the point with my project where I decided to simplify some of the js functions where I am looking for a parent in a DOM tree, then drill down to the elements many many times in one function. instead I though I will make instances of a function which will keep some data so then I can refer and operate on objects in easy way. I got it working but as I was going along, I decided to extend functionality and add some extra functions like getElementsByClassNameThenTagName.
I loop through the arrays and if add matching elements to the array.
I have noticed (sadly only now) that I am creating an array with elements rather than HTML collection. As a results, I cannot refer to the objects in my findings by typing buttons['reset'].disabled = false;. I can access my reset button by buttons[3].disabled = false; but this would cause a lot of inconvenience.
I am therefore looking for a way to convert my array with object into a HTML collection.
Please see below my current function:
this.getElementsByClassNameThenTagName = function (elementClass, elementTag) {
if (parentNode == null) {
this.init();
}
var results = [];
var regexStr = elementClass;
var regex = new RegExp(regexStr);
var x = moduleNode.getElementsByClassName(elementClass);
// console.log(x);
var y;
for ( var i = 0; i < x.length; i++ ) {
// console.log(i);
y = x[i].getElementsByTagName(elementTag);
// console.log(y);
for (var k=0; k<y.length; k++){
// console.log(y[k]);
results.push(y[k]);
}
// console.log(results);
}
return results;
};
Any suggestions please?
Thanks.
this.getElementsByClassNameThenTagName = function (elementClass, elementTag) {
if (parentNode == null) {
this.init();
}
var results = {}; // thid should be an object (collection)
var x = moduleNode.querySelectorAll("." + elementClass + " " + elementTag);
x.forEach(function(y) {
var name = y.getAttribute("name"); // if you really sure that all the matched elements have names
results[name] = y;
});
return results;
};
Now you can use the results array like this:
var someElement = results['some name'];
NOTE: All the matched elements x should have a name attribute, and all the name attributes of the matched elements should be unique.
I have issue with two type of arrays value comparison, here is
tagNames = [];
tagNames.push('61');
cmt_wrds = '61'.replace(/[`~!##$%^&*()_|+\-=?;:'",،؛«».<>\{\}\[\]\\\/]/gi, ' ').match(/\S+/g);
if ( tagNames[0] == cmt_wrds[0] ) { // issue is here
console.log('yes'); // --> nothing
};
If you log your variables you will see that they are a bit different. It puts
'\u200f'
symbol which is the Right-To-Left Mark.
var tagNames = [];
tagNames.push('61');
cmt_wrds = '61'.replace(/[`~!##$%^&*()_|+\-=?;\u200f:'",،؛«».<>\{\}\[\]\\\/]/gi, ' ').match(/\S+/g);
console.log(tagNames);
console.log(cmt_wrds);
console.log(tagNames[0] === cmt_wrds[0]); // returns false, because they are different
Batter use with some varibale to give ah input data It will solve your problem.
(function () {
tagNames = [];
tagNames.push('61');
var datas ="61";//variable declaration
cmt_wrds = datas.replace(/[`~!##$%^&*()_|+\-=?;:'",،؛«».<>\{\}\[\]\\\/]/gi, ' ').match(/\S+/g);
if ( tagNames[0] == cmt_wrds[0]) { // issue is here
console.log('yes'); // --> nothing
};
})()
If your show their length, here is the difference.
tagNames[0].length is 2
cmt_wrds[0].length is 4
tagNames = [];
tagNames.push('61');
cmt_wrds = '61'.replace(/[`~!##$%^&*()_|+\-=?;:'",،؛«».<>\{\}\[\]\\\/]/gi, ' ').match(/\S+/g);
if ( tagNames[0] == cmt_wrds[0] ) { // issue is here
console.log('yes'); // --> nothing
};
console.log(tagNames[0].length);
console.log(cmt_wrds[0].length);
Is it possible to select elements based on mutually exclusive data attributes, for example: I'd like to .show() any divs with data attribute of country="united-kingdom" and also with type="partner" OR "director"? Something like:
$('.post[data-country="united-kingdom"]&[data-type="partner,director"]').show();
or
$('.post[data-country="united-kingdom"]&[data-type="partner"]or[data-type="director"]').show();
I'd like to .show() any divs with data attribute of country="united-kingdom" and also with type="partner" OR "director"?
Then you want a selector group:
$('.post[data-country="united-kingdom"][data-type="partner"], .post[data-country="united-kingdom"][data-type="director"]').show();
That says: Match any element which
Has class post, has data-country set to united-kingdom, and has data-type set to partner
or
Has class post, has data-country set to united-kingdom, and has data-type set to director
The "or" part comes from the ,, which is what makes it a selector group rather than a single selector.
In a comment, you've said:
The user might select ten or more of each taxonomy term which requires generating loads of permutations of this conditional.
In that case, you may be better off with filter:
var countries = ["united-kingdom"]; // This would be created from inputs
var types = ["partner", "director"]; // This too
then
var results = $(".post[data-country][data-type]").filter(function() {
var $this = $(this);
return countries.indexOf($this.attr("data-country") != -1 &&
types.indexOf($this.attr("data-type") != -1;
});
In ES2016 or above, you could use Array#includes — which gives you a simple boolean — instead of Array#indexOf which you have to check against -1; and there'a polyfill you can use in ES2015 and earlier...:
var results = $(".post[data-country][data-type]").filter(function() {
var $this = $(this);
return countries.includes($this.attr("data-country") &&
types.includes($this.attr("data-type");
});
This can even be taken further:
var criteria = {};
// From inputs, ...
criteria["country"] = ["united-kingdom"];
criteria["type"] = ["parter", "director"];
then
var keys = Object.keys(criteria);
var selector= ".post" + keys.map(function(key) {
return "[data-" + key + "]";
}).join();
var results = $(selector).filter(function() {
var $this = $(this);
return keys.every(function(key) {
return criteria[key].includes($this.attr("data-" + key));
});
});
And as long as we're thinking about ES2015 and ES2016:
const keys = Object.keys(criteria);
const results = $(selector).filter(() => {
const $this = $(this);
return keys.every(key => criteria[key].includes($this.attr("data-" + key)));
});
or if you really want to go crazy:
const keys = Object.keys(criteria);
const results = $(selector).filter(() =>
keys.every(key => criteria[key].includes(this.getAttribute("data-" + key)))
);
You can add multiple selectors separated by a comma
$('.post[data-country="united-kingdom"][data-type="artist"], .post[data-country="united-kingdom"][data-type="partner"]').show();
Or use a filter with a selector
$('.post[data-country="united-kingdom"]').filter('[data-type="artist"], [data-type="partner"]').show();
or a filter with a callback
var countries = ["united-kingdom", "india", "france"],
types = ["artist", "partner"];
$('.post').filter(function() {
return types.indexOf( $(this).data('type') ) !== -1 &&
contries.indexOf( $(this).data('coutry') ) !== -1;
}).show()
Is there an easy way to fix this code:
title_1 = $(this).closest('tr').find('td').html();
title_2 = $(this).closest('tr').find('td').next().html();
title_3 = $(this).closest('tr').find('td').next().next().html();
question = question.replace(/{title_1}/g, title_1);
question = question.replace(/{title_2}/g, title_2);
question = question.replace(/{title_3}/g, title_3);
So it isn't so dully (repeated) and can cover n occurences of title_ pattern?
I'm a beginner Javascript developer and a complete regular expressions newbie (actually, they scare me! :|), so I'm unable to do this by myself. I've tried to look for an inspiration in different languages, but failed.
You can use a function in the replace, to get the value depending on what you find:
question = question.replace(/{title_(\d+)}/g, $.proxy(function(x, m){
return $(this).closest('tr').find('td:eq('+(m-1)+')').html();
}, this));
Demo: http://jsfiddle.net/n3qrL/
String.prototype.replace() could take a function as second parameter.
var $this = $(this);
question = question.replace(/\{title_(\d+)\}/g, function(match, n) {
return $this.closest('tr').find('td').eq(n - 1).html();
});
Demo Here
Try this ,
Generalized for getting all td tag's text value :
$("table").find("tr").each(function(){
$(this).find("td").each(function(){
alert($(this).html());
var txt=$(this).html();
//var pattern="/{"+txt+"}/g";
//question = question.replace(pattern, txt);
});
});
NB. In your question you have not mentioned the value for 'question' . please define value for 'question'
It seems to me that you want to get the text content of the first three cells of a table row and use it to replace the content of a string, and that this is an element somewhere in the row. So you can do:
var n = 3; // number of cells to get the text of
var textArray = [];
var tr = $(this).closest('tr')[0];
var reString;
for (var i=0; i<n; i++) {
reString = '{title_' + (i+1) + '}';
question = question.replace(reString, tr.cells[i].textContent);
}
If you wish to avoid jQuery's closest, you can use a simple function like:
function upTo(el, tagName) {
tagName = tagName.toLowerCase();
do {
el = el.parentNode;
if (el.tagName && el.tagName.toLowerCase() == tagName) {
return el;
}
} while (el.parentNode)
}
then:
var tr = upTo(this, 'tr');
I need a way to add an object into another object. Normally this is quite simple with just
obj[property] = {'name': bob, 'height': tall}
however the object in question is nested so the following would be required:
obj[prop1][prop2] = {'name': bob, 'height': tall}
The clincher though, is that the nesting is variable. That is that I don't know how deeply each new object will be nested before runtime.
Basically I will be generating a string that represents an object path like
"object.secondObj.thirdObj.fourthObj"
and then I need to set data inside the fourth object, but I can't use the bracket [] method because I don't know how many brackets are required beforehand. Is there a way to do this?
I am using jQuery as well, if that's necessary.
Sure, you can either use recursion, or simple iteration. I like recursion better. The following examples are meant to be proof-of-concept, and probably shouldn't be used in production.
var setDeepValue = function(obj, path, value) {
if (path.indexOf('.') === -1) {
obj[path] = value;
return;
}
var dotIndex = path.indexOf('.');
obj = obj[path.substr(0, dotIndex)];
return setDeepValue(obj, path.substr(dotIndex + 1), value);
};
But recursion isn't necessary, because in JavaScript you can just change references.
var objPath = 'secondObj.thirdobj.fourthObj';
var valueToAdd = 'woot';
var topLevelObj = {};
var attributes = objPath.split('.');
var curObj = topLevelObj;
for (var i = 0; i < attributes.length; i++) {
var attr = attributes[i];
if (typeof curObj[attr] === 'undefined') {
curObj[attr] = {};
}
curObj = curObj[attr];
if (i === (attributes.length - 1)) {
// We're at the end - set the value!
curObj['awesomeAttribute'] = valueToAdd;
}
}
Instead of generating a string...
var o="object";
//code
o+=".secondObj";
//code
o+=".thirdObj";
//code
o+=".fourthObj";
...you could do
var o=object;
//code
o=o.secondObj;
//code
o=o.thirdObj;
//code
o=o.fourthObj;
Then you can add data like this:
o.myprop='myvalue';
And object will be updated with the changes.
See it here: http://jsfiddle.net/rFuyG/