nextAll() returns undefined - jQuery - javascript

I am making organizatoinal-chart-like structure. All divs have unique ID and a class that matches the ID of their parent (if they have one) like so:
<div id="title1" class="container"></div>
<div id="title4" class="container child title3"></div>
<div id="title3" class="container child title2"></div>
<div id="title2" class="container child title1"></div>
Idea is to order these in chart by absolutely positioning divs below their parent div, so they will be rendered in the right order.
Problem with the code below is that the $test_str returns as undefined, even if I pass .child as selector... and I am stuck.
$containers = $('.container');
$test = [];
for ( i = 0; i < $containers.length; i++ ) {
$test_str = $($containers[i]).nextAll('.' + $containers[i].id);
$test.push($test_str);
console.log($containers[i].id + ' is parent ' + $test[i].id);
//$test[i].id is returned as undefined;
}
Here is the fiddle

I would take an approach of just moving the divs using appendTo or insertAfter instead of trying to absolutely position them (which I think would be more difficult). This example uses appendTo since it creates an actual parent-child structure instead simply re-ordering.
$.each($('.container'), function(ind, val) {
$.each($('.' + val.id), function(i,v) {
console.log(val.id + ' is parent ' + v.id);
$(v).appendTo(val);
});
});
JSFiddle

If you output the $test_str to the console, the element info is under context, so you'll want to get the id from $test[i].context.id
$containers = $('.container');
$test = [];
for ( i = 0; i < $containers.length; i++ ) {
jQuery.fn.init();
$test_str = $($containers[i]).nextAll('.' + $containers[i].id);
$test.push($test_str);
console.log($containers[i].id + ' is parent ' + $test[i].context.id);
}

The problem you have is that nextAll() returns a sequence of items and you are treating it as a single item. The second problem you are probably going to run into is you are using nextAll() instead of siblings().
siblings
Description: Get the siblings of each element in the set of matched elements, optionally filtered by a selector.
nextAll() returns only successors at the same level and not true siblings.
Description: Get all following siblings of each element in the set of matched elements, optionally filtered by a selector.
Here is the code showing a way to get the result you want. I took out the intermediate array for the time being as it would just further complicate this.
$containers = $('.container');
$test = [];
for ( i = 0; i < $containers.length; i++ ) {
$test_str = $($containers[i]).siblings('.' + $containers[i].id);
console.log($test_str);
for(j = 0; j < $test_str.length; j++){
console.log($containers[i].id + ' is parent ' + $test_str[j].id);
}
}
Remember when dealing with JQuery that most of the time the functions will return a sequence of items instead of a single item, even if there is only one match.

Related

Pure JS: Store list of data- attributes in array, then iterate

I'm fairly new to JS and I can do DOM manipulation and if/else statements by hand. Now I'm trying for something out of my league, combining iteration with arrays, and I have a bit of a hard time understanding both of them.
With this in mind: Considering this div: <div id="firstAnchor"> would act as an anchor to this link:
I want to store the ID of these div's (id's should be able to be anything):
<div id="firstAnchor" style="display: inline-block;">First title</div>
<div id="secondAnchor" style="display: inline-block;">Second title</div>
<div id="thirdAnchor" style="display: inline-block;">Third title</div>
into an array, and then create these three links automatically* placed in a div called "anchorLinks":
Link to first title
Link to second title
Link to third title
How would I go about this?
*
for example within this function:
(function create_anchor_link_list() {
//placed here
})();
Edit:
Here is what I have tried to begin with. I first had data-anchor="firstAnchor" etc. on my div elements until I realized I couldn't link to div elements based on data- attributes values. So with the data- attributes I tried:
(function anchorsInPage2(attrib) {
console.log("=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#");
console.log("=#=#=#=#=# anchorsInPage2 function #=#=#=#=#");
console.log("=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#");
console.log(" ");
var elements = document.getElementsByTagName("*");
var foundelements = [];
for (var i = 0; i < elements.length; i++) {
if (elements[i].attributes.length > 0) {
for (var x = 0; x < elements[i].attributes.length; x++) {
if (elements[i].attributes[x].name === attrib) {
foundelements.push(elements[i]);
}
}
}
}
return foundelements;
console.log(" ");
console.log("=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#");
console.log("=#=#=#=#=# / anchorsInPage2 function #=#=#=#=#");
console.log("=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#");
})();
function anchorsInPage3() {
console.log("=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#");
console.log("=#=#=#=#=# anchorsInPage3 function #=#=#=#=#");
console.log("=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#");
console.log(" ");
var elements = document.getElementsByTagName("*");
var foundelements = [];
for (var i = 0; i < elements.length; i++) {
if (elements[i].attributes.length > 0) {
for (var x = 0; x < elements[i].attributes.length; x++) {
if (elements[i].attributes[x].name === "anchor") {
foundelements.push(elements[i]);
}
}
}
}
return foundelements;
console.log(" ");
console.log("=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#");
console.log("=#=#=#=#=# / anchorsInPage3 function #=#=#=#=#");
console.log("=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#");
}
(function anchorsInPage1() {
console.log("=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#");
console.log("=#=#=#=#=# anchorsInPage1 function #=#=#=#=#");
console.log("=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#");
console.log(" ");
var anchors = document.querySelectorAll('[anchor]');
for(var i in anchors){
console.log(i);
}
console.log(" ");
console.log("=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#");
console.log("=#=#=#=#=# / anchorsInPage1 function #=#=#=#=#");
console.log("=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#");
})();
First update after further testing:
Barmar's example was used. The text below is a direct answer to Barmar (too long for the other comment field)
Test: http://jsfiddle.net/e5u03g4p/5/
My reply:
With the first variable you found all element with the attribute data-anchor, so I guess the brackets in querySelectorAll tells it which specific attribute we mean instead of what elements ID's we want, which is the "standard" writing document.querySelectorAll("tagName") instead of document.querySelectorAll("[attributeName]").
With the second variable you found the first element with the ID of anchorLinks. The hashtag is needed to specify ID as querySelector represents div so the result is div#anchorLinks(?).
You then take the variable anchors (which results in an array of the data-anchor value of the div's with the data-anchor attribute) and for each of them, a function triggers where the d argument of the function equals the element ID of the elements with the data-anchor attribute. Everything within this function repeats for each of the elements with data-anchor attribute (ie. the variable anchors).
What's happening within the function is:
-You create a variable (a) which contains the element creation of an <a> element
-You then set the href attribute of the newly created <a> element to the ID
of the data-anchor elements.
-I then assign the attribute title of the <a> elements to the content of the data-anchor elements (instead of the original thought where it was textContent that was set to the <a> elements`as I want the links to be images instead of text)
-I then also added a new class attribute to the <a> elements in order to style them
If you used data-anchor="something" in your DIVs, then you should use
var anchors = document.querySelectorAll('[data-anchor]');
not [anchor].
You can then loop over them with forEach()
var anchorLinks = document.querySelector("#anchorLinks");
anchors.forEach(function(d) {
var a = document.createElement('a');
a.href = '#' + d.id;
a.textContent = 'Link to ' + d.textContent.toLowerCase();
anchorLinks.appendChild(a);
});
If you craft the query selector correctly you can get all of the "anchor" elements at once, then iterate over them to add the relevant links.
var links = document.getElementById('anchorLinks');
document.querySelectorAll('#anchors div[id]').forEach(function(anchor) {
var link = document.createElement('a');
link.href = '#' + anchor.id;
link.textContent = 'Link for ' + anchor.textContent;
links.appendChild(link);
});
<div id="anchors">
<div id="firstAnchor" style="display: inline-block;">First title</div>
<div id="secondAnchor" style="display: inline-block;">Second title</div>
<div id="thirdAnchor" style="display: inline-block;">Third title</div>
</div>
<div id="anchorLinks">
</div>
How about this:
document.getElementsByTagName('div').forEach(function(d) {
var a = document.createElement('a');
a.setAttribute('href', '#' + d.id);
a.innerHTML = 'Link to ' + d.textContent.toLowerCase();
document.getElementById('anchorLinks').appendChild(a);
});
Or if you have more divs (of course) and they have a specific class, you can do:
document.getElementsByClassName('your-class-name').forEach(function(d) {
var a = document.createElement('a');
a.setAttribute('href', '#' + d.id);
a.innerHTML = 'Link to ' + d.textContent.toLowerCase();
document.getElementById('anchorLinks').appendChild(a);
});

querySelectorAll.style does not work

I am writing something in JavaScript that I need to use querySelectorAll.style but it always returns undefined, but it works perfectly with querySelector.style. How can I make it work properly so I can set the style?
document.querySelector("div#tabs" + tabId + "> div.page").style.display = 'none'; //works
document.querySelectorAll("div#tabs" + tabId + "> div.page").style.display = 'none';// doesn't work
querySelector:
Returns the first element within the document...
querySelectorAll:
Returns a list of the elements within the document...
IE in the first one, you're operating on a single element, which does have a style property. The second one is a list of elements, so you need to loop over that list applying the style:
var els = document.querySelectorAll("div#tabs" + tabId + "> div.page");
for (var x = 0; x < els.length; x++)
els[x].style.display = 'none';
querySelectorAll returns a list of elements rather than a single one.
So this should work to apply the style to the first element found:
document.querySelectorAll("div#tabs" + tabId + "> div.page")[0].style.display = 'none'; // First element
querySelectorAll returns a html collection of elements, not a single element, so you need to loop over the results:
Array.from(document.querySelectorAll("div#tabs" + tabId + "> div.page"))
.forEach(function(val) {
val.style.display = 'none';
});

Deleting all div children from a parent without a for loop in javascript

I have the following code in my javascript:
for (var a = 0; a < cnt; a++) {
var element = document.getElementById("button" + a).getElementsByTagName("div");
for (index = element.length - 1; index >= 0; index--) {
element[index].parentNode.removeChild(element[index]);
}
$("#button" + a).append("Some large html data");
}
I am deleting all the children from parent id "button0","button1"... and so on, which are divs.
and then appending new data to those parents.
However, this particular piece of code takes a long time to execute when the cnt is more than 200, which it usually is. How will I be able to speed it up? Is there an alternative way to delete all children divs without going through each of it?
<div class="main">
<p>hello p1</p>
<p>hello p2</p>
<span> hello world this is span </span>
</div>
$('.main p').remove(); // any number of depths
$('.main > p').remove(); // immediate children
Try this : You can use children selector to remove them, no need to iterate through children.
for (var a = 0; a < cnt; a++) {
//remove div elements inside button
$("#button"+a+" > div").remove();
$("#button" + a).append("Some large html data");
}
DEMO
IF you can have particular class to button div then you can get rid of for loop.
Lets say class="buttonDiv" is assigned to all button div, for example
<div id="button0" class="buttonDiv">
Now your jQuery script to remove child div will be like
$('div.buttonDiv').each(function(){
$(this).children("div").remove();
$(this).append("Some large html data");
});
DEMO with Class
You can use jQuery to delete them, but I don't know how much faster it will be. Under the covers it has to do pretty much the same work:
for (var a = 0; a < cnt; a++) {
$("#button" + a + " div").remove().end().append("Some large html data");
}
It would be much easier if you just add one class to all the buttons you want to remove the children of. Lets say you add the class button to all of them. Then you could just do this:
$('.button > div').remove(); // Removes all direct children divs of the .button element.
Or
$('.button div').remove(); // Removes all divs inside the `.button` element.

add a number for each div

Hi I'd like some advice with what I'm trying to achieve.
I currently have this:
<div class="thumbnail">
<img src="thumbnail_1.jpg" />
</div>
For each .thumbnail I would like to prepend an index number with a span. Achieving this:
<div class="thumbnail">
<span class="index">001</span>
<img src="thumbnail_1.jpg" />
</div>
<div class="thumbnail">
<span class="index">002</span>
<img src="thumbnail_2.jpg" />
</div>
Thanks heaps.
$('div.thumbnail').each(function(i) {
var num = zeroPad(i + 1, 3);
$(this).prepend($('<span/>', {
'class': 'index',
'text': num
}));
});
The .each() method iterates over the elements and the callback receives the zero-based index of the element - so there you have your counter. The element itself is available as this (or as the second function argument, but you don't really need that). .prepend() inserts the passed element/string at the beginning of the element.
For a zeroPad function, simply search for "pad number javascript" in Google or here on SO and you'll find quite a few functions. Here's one for example:
function zeroPad(num, numZeros) {
var n = Math.abs(num);
var zeros = Math.max(0, numZeros - Math.floor(n).toString().length );
var zeroString = Math.pow(10,zeros).toString().substr(1);
if( num < 0 ) {
zeroString = '-' + zeroString;
}
return zeroString + n;
}
Along with the other solutions, I prefer the following (personal taste)
$('div.thumbnail').prepend(function (index) {
index = '000' + (index + 1);
return '<span class=index>' + index.substr(index.length - 3) + '</span>';
});
The prepend method takes a function that should return the html/DOM object that is to be prepended. See more in the docs.
Edit: As Michael Durrant commented, you might be wanting the numbers in the img's src attribute, not sequential numbers. If that is the case, the following should have you covered.
$('div.thumbnail > img').before(function () {
var index = this.src.match(/\d+/);
if (index === null) return;
index = '000' + index;
return '<span class=index>' + index.substr(index.length - 3) + '</span>';
});
Here, we add the span before the img elements. See the before documentation for more.
You can select all elements with the thumbnail class, loop over them, and prepend a span to each element containing the index.
// Select all elements with class .thumbnail and loop over them
$(".thumbnail").each(function(i, elm) {
// Prepend a index span to each element
$(elm).prepend('<span class="index">' + i + '</span>");
});
In this case the index will be number will be zero-based. If you like the index to start with 1, you can change the middle row to this: $(elm).prepend('<span class="index">' + (i + 1) + '</span>");
Try this:
$('.thumbnail').each(function(index) {
$('<span/>', {
'class': 'index',
text: "%03d".sprintf(index + 1)
}).prependTo(this);
});
Note that it won't add leading zeroes, as is.
I like the JSXT String.js module which would allow you to write "%03d".sprintf(index + 1)
Working demo at http://jsfiddle.net/SqQcs/1/
EDIT code has changed from first attempt - forgot that $(<tag>, { ... }) syntax only works in the jQuery constructor, and not in the generalised jQuery argument case.

JQuery Table Manipulation Inserting rows

What I want to do, in short, is from $(this) being a table row, find the next table row with a class of "example" (not necessarily a sibling).
I.E. use next() to find the next row with a class of "example" which isn't a sibling.
HTML:
<table>
<tr><td>One</td></tr>
<tr class="current"><td>Two</td></tr>
<tr><td>Three</td></tr>
<tr><td>Four</td></tr>
<tr class="target"><td>Five</td></tr>
<tr><td>Six</td></tr>
</table>
JavaScript:
var current = $('.current').next();
while(current.size() && !current.hasClass('target')) {
current = current.next();
}
current.css('color', '#0f0');
OR
$('.current').nextAll('.target').last().css('color', '#0f0');
If you're building those <tr>s from strings, you could do something like that:
var x = '';
for(var i = 0; i < 3; i++) {
x += '<li>Test ' + i + '</li>';
}
$(x).appendTo('#test');
So instead of inserting the table rows one by one, put them together as one string, make a jQuery object from that string and attach that to your table.
This also helps you with performance, since you edit the DOM only once.

Categories

Resources