I need help working with a large list of sibling elements with different class names.
Getting the amount of elements with the same class name and putting them in an array
Finding first element in that class group (this can be number or name).
Statement that runs a function: if element = first element of group do console.log("first element");
Here's an example of the first 3 classes but this will go from groupA to Groupz
<div class = 'slider'>
<div class = 'item1 groupA'> <!-- Start Group A -->
<img src='xyz' />
</div>
<div class = 'item1 groupA'>
<img src='xyz' />
</div>
<div class = 'item1 groupA'>
<img src='xyz' />
</div>
<div class = 'item1 groupA'>
<img src='xyz' />
</div>
<div class = 'item1 groupB'> <!-- Start Group B -->
<img src='xyz' />
</div>
<div class = 'item1 groupB'>
<img src='xyz' />
</div>
<div class = 'item1 groupB'>
<img src='xyz' />
</div>
<div class = 'item1 groupC'> <!-- Start Group C -->
<img src='xyz' />
</div>
<div class = 'item1 groupC'>
<img src='xyz' />
</div> <!-- All the way to group Z -->
</div>
Edit: Your requirement is very specific. Below is just a sample to just loop thru all childrens and store the count and first element in the matching count. Let me
$(function () {
$.fn.benton = function () {
//just the immediate childrens
var $chds = $(this).children();
var lc = {
firstEl: {},
classCount: {}
};
$.each ($chds, function (idx, el) {
if (el.className) {
var tokens = el.className.split(' ');
for (var i = 0; i < tokens.length; i++) {
if (lc.classCount.hasOwnProperty(tokens[i])) {
lc.classCount[tokens[i]] += 1;
} else {
lc.classCount[tokens[i]] = 1;
lc.firstEl[tokens[i]] = $(el);
}
}
}
});
return lc;
};
var stats = $('.slider').benton();
console.log(stats.classCount['groupA']);
stats.firstEl['item1'].css({border: '1px solid red', width: 100, height: 10});
});
DEMO: http://jsfiddle.net/LhwQ4/1/
I think what you need is to use context of slider to get the child elements.. see below,
var $slider = $('.slider')
Now using the $slider context,
$('.groupA', $slider)
//Returns Array of jQuery object with elements has class `groupA`
$('.groupA:first', $slider)
//Returns first element in collection of element with class `groupA`
To get all elements with the same class name, you would only have to use a simple jQuery selector. The returned value is an array containing all matching elements.
var groupA = $(".groupA");
To get the number of items you need only access the length parameter of the array.
var groupALength = groupA.length;
If you want to extract only the first element of any matched elements, you can use jQuery's :first selector.
var firstElement = $(".groupA:first");
var groups = {};
$(".slider").children().each(function(i, el) {
var classes = el.className.split(/\s+/);
for (var i=0; i<classes.length; i++)
if (classes[i] in groups)
groups[classes[i]].push(el);
else
groups[classes[i]] = [el];
});
Now, you can access all elements of a group via groups["groupA"] etc (jQuery collection: $(groups["groupB"])) and the first one via groups["groupC"][0]. The amount of elements in a group is just the length of the array.
Notice that this puts all elements in the group "item1" - I don't know what you need that class for.
Ok, so this solution is quite sensitive. I'm making a few assumptions about your HTML.
In your example you gave each item a class of item1. I am assuming that this is just an issue of copying and pasting the element. Each "item" should have the same class so that you can retrieve all the items with one selector. For my example, I'm assuming a class of item.
There should be only this item class plus an additional "group" class. Any other class given to the item will render this solution invalid.
// fetch ALL items
var allItems = $(".item");
// initialize groups array
var groups = {};
$.each(allItems,function(index,elem){
var item = $(this);
var itemClass = item.attr('class');
// remove the "item" class and any leftover whitespace
itemClass = $.trim(itemClass.replace('item','')); // should now be groupA/groupB...
// add item to array at the index of the group
if (groups[itemClass] == undefined){
groups[itemClass] = [];
}
groups[itemClass].push(item);
});
You should now be left with an array of arrays containing all the items. To see this in action, you can check out this jsFiddle.
Related
Consider the following hierarchy in DOM
<div class="bodyCells">
<div style="foo">
<div style="foo">
<div style="foo1"> 'contains the list of text elements I want to scrape' </div>
<div style="foo2"> 'contains the list of text elements I want to scrape' </div>
</div>
<div style="foo">
<div style="foo3"> 'contains the list of text elements I want to scrape' </div>
<div style="foo4"> 'contains the list of text elements I want to scrape' </div>
</div>
By using class name bodyCells, I need to scrape out the data from each of the divs one at a time (i.e) Initially from 1st div, then from the next div and so on and store it in separate arrays. How can I possibly achieve this? (using puppeteer)
NOTE: I have tried using class name directly to achieve this but, it gives all the texts in a single array. I need to get data from each tag separately in different arrays.
Expected output:
array1=["text present within style="foo1" div tag"]
array2=["text present within style="foo2" div tag"]
array3=["text present within style="foo3" div tag"]
array4=["text present within style="foo4" div tag"]
As you noted, you can fetch each of the texts in a single array using the class name. Next, if you iterate over each of those, you can create a separate array for each subsection.
I created a fiddle here - https://jsfiddle.net/32bnoey6/ - with this example code:
const cells = document.getElementsByClassName('bodyCells');
const scrapedElements = [];
for (var i = 0; i < cells.length; i++) {
const item = cells[i];
for (var j = 0; j < item.children.length; j++) {
const outerDiv = item.children[j];
const innerDivs = outerDiv.children;
for (var k = 0; k < innerDivs.length; k++) {
const targetDiv = innerDivs[k];
scrapedElements.push([targetDiv.innerHTML]);
}
}
}
console.log(scrapedElements);
<body>
<div class = "order-1-a">
<div class = "order 2-a">
<div class = "order 3-a"></div>
</div>
<div class = "order 2-b"></div>
<div class = "order 2-c"></div>
<div class = "order 2-d"></div>
</div>
<div class = "order-1-b"></div>
</body>
If I want a div to wrap only class "order-2-a" + being the first child of "class-1-a", how should I script the div with JavaScript?
Probably your best bet is to:
Create a new Element with .createElement().
Append 2-a to the new Element with .appendChild().
Insert the new element before 2b with .insertBefore().
var one_a = document.getElementsByClassName("order-1-a")[0];
var two_a = document.getElementsByClassName("order-2-a")[0];
var two_b = document.getElementsByClassName("order-2-b")[0];
var new_node = document.createElement("div");
new_node.appendChild(two_a);
one_a.insertBefore(new_node, two_b);
console.log(one_a.innerHTML);
<body>
<div class="order-1-a">
<div class="order-2-a">
<div class="order-3-a"></div>
</div>
<div class="order-2-b"></div>
<div class="order-2-c"></div>
<div class="order-2-d"></div>
</div>
<div class="order-1-b"></div>
</body>
This provides the structure you're looking for (albeit not displayed well with console.log()).
Also, please be aware that class names cannot start with numbers, and may yield unexpected results. I've updated most of your classes to start with order in my example, as is with your order-1-a class.
Hope this helps!
You can create a general wrapping function based on a selector. It should get the subject node, then its parent and either it's next sibling or null if there isn't one.
Then create an element of the required type, append the subject node and insert it before the next sibling or as the last node if there wasn't one.
PS.
I've modified the class names to be valid, they can't start with a digit.
// Wrap element with selector in element with tagName
function wrapEl(selector, tagName) {
var node = document.querySelector(selector);
// If there is no subject node, return
if (!node) return;
// Get parent and sibling (or null if there isn't one)
var parent = node.parentNode;
var sibling = node.nextSibling;
// Append stuff
var wrapper = document.createElement('tagName');
wrapper.textContent = 'inserted wrapper'; // Just to show it's there
wrapper.appendChild(node);
parent.insertBefore(wrapper, sibling);
}
window.onload = function() {
wrapEl('.order-2-a', 'div');
}
<body>
<div class = "order-1-a">
<div class = "order-2-a">
<div class = "order-3-a"></div>
</div>
<div class = "order-2-b"></div>
<div class = "order 2-c"></div>
<div class = "order 2-d"></div>
</div>
<div class = "order-1-b"></div>
</body>
I have this html code on page:
<div style="display:none" id="roles">
<span>Manager</span>
<span>Seller</span>
</div>
And i jas want to get array of string between spans element.
var roles = document.getElementById("roles").innerText.match("what i should get here"); // output roles = ["Manager", "Seller"]
var roles = Array.prototype.slice.call(document.getElementById('roles').getElementsByTagName('span')).map(function(node) {
return node.innerText || node.textContent;
});
console.log(roles);
<div style="display:none" id="roles">
<span>Manager</span>
<span>Seller</span>
</div>
You need to iterate span elements and get its text.
//Get the span elements
var spans = document.querySelectorAll("#roles span");
var roles = [];
//Iterate the elements
for (var i = 0; i < spans.length; i++) {
//fetch textContent and push it to array
roles.push(spans[i].textContent);
}
console.log(roles)
<div style="display:none" id="roles">
<span>Manager</span>
<span>Seller</span>
</div>
You can also use as suggested by #Tushar
//Get the span elements
var spans = document.querySelectorAll("#roles span");
var roles = Array.from(spans).map(s => s.textContent);
console.log(roles)
<div style="display:none" id="roles">
<span>Manager</span>
<span>Seller</span>
</div>
Get all span elements using querySelectorAll then convert it to array with help of Array.from method(older browser use [].slice.call) and now generate the result array using Array#map method.
// for older browser use `[].slice.call(...` instead of `Array.from(...`
var res = Array.from(document.querySelectorAll('#roles span')).map(function(e) {
return e.textContent;
});
console.log(res);
<div style="display:none" id="roles">
<span>Manager</span>
<span>Seller</span>
</div>
<div id="exmpl1">
<span id="1st"> A1</span>
<span id="2nd"> A2</span>
<span id="3rd"> B1</span>
<span id="4th"> B2</span>
<span id="5th"> C1</span>
</div>
var spans = document.getElementById('exmpl1').getElementsByTagName('span'),
obj = {};
for (var i = 0, j = spans.length; i < j; i++) {
obj[spans[i].id] = spans[i].textContent || spans[i].innerText;
}
console.log(obj);
Here is another solution :
var spans;
document.querySelectorAll('#roles > span').forEach(function(element){
spans.push(element.textContent);
});
I would recommend first finding the descendents of the roles div and then iterating through them in a for loop to post the results to an array.
//Define the parent div
var parent = document.getElementById('roles');
//Find the children of the parent div
var children = parent.getElementsByTagName('span');
//Count how many children there are within the parent div
var totalChildren = children.length;
//Declare a new array to store the roles in
var rolesArray = [];
//And let the for loop push each role into the array
for (var i = 0; i < totalChildren; i++) {
rolesArray.push(children[i].innerText);
}
Hey I need to count the nested divs with a given class name. For example:
<div id = "divA">
<div class = "anotherName"></div>
<div class = "toBeCounted"></div>
<div class = "someName"></div>
<div class = "toBeCounted"></div>
<div class = "toBeCounted"></div>
<div class = ""></div>
</div>
<div id = "divB">
<div class = ""></div>
<div class = "toBeCounted"></div>
<div class = ""></div>
<div class = "toBeCounted"></div>
</div>
So if I want to count "toBeCounted" I would get 3 for divA and 2 for divB.
You can use .querySelectorAll() and check the length of the result:
var divAcount = document.querySelectorAll("#divA > .toBeCounted").length;
The > relation insists that the .toBeCounted elements are immediate children of divA. If that's not what you want, and any toBeCounted div within divA should count, you'd just leave out the >.
try this pure javascript code
var countinDivA = document.getElementById("divA").getElementsByClassName("toBeCounted").length;
var countinDivB = document.getElementById("divB").getElementsByClassName("toBeCounted").length;
With Jquery this can be easily achieved by using
var countA = $("#divA .toBeCounted").length;
var countB = $("#divB .toBeCounted").length;
If you don't know the id's of the parents ahead of time, this may prove useful:
var parents = [],
counted = document.getElementsByClassName("toBeCounted");
for (var i=0; i < counted.length; i++ ) {
var id = counted[i].parentNode.id;
if ( !parents[id] ) parents[id] = 1
else parents[id]++;
}
[ divA: 3, divB: 2 ]
I'm creating a set of tabs based upon an existing set of categories with the below JS. I need to extend this to target specific id's within the DIV id based upon values from a JS array.
$("#categories div[id^=category]:not(:first)", this).hide();
var cats = $('#categories div[id^=category]');
cats.each(function () {
var anch = $(this).find('h3 a').eq(1).clone();
anch[0].rel = this.id;
$('<li/>').append(anch).appendTo('#tabs');
});
The html:
<div id="category_1">
<h3 class="maintitle">
<a class="toggle">..</a>
Cat 1 Title
</h3>
<div>
...
</div>
</div>
<div id="category_2">
<h3 class="maintitle">
<a class="toggle">..</a>
Cat 2 Title
</h3>
<div>
...
</div>
</div>
I've got a JS array ready by adding:
var catsList = '{$cats}'; // comma separated list of numbers generated in PHP - returns 1,4,8 currently.
var catsArray = catsList.split(',');
How would I convert the below, to check for each item within catsArray ?
var cats = $('#categories div[id^=category]');
Something like
var cats = $('#categories div[id^=category_'+catsArray+']');
but obviously checking each item within the array and not the entire array as that's doing.
You could use that as IDs have to be unique on context page:
var cats = $('#category_'+catsArray.join(',#category_'));
DEMO
you probably want the each function
$.each(catsArray,function(index, item) {
var cats = $('#categories div[id^=category_'+item+']');
});
Depending on how you using this a for loop will do it also:
for (var i = 0; i < catsArray.length; i++) {
var catIndex = catsArray[i];
var cats = $('#categories div[id^=category_'+catIndex +']');
}