Iteration in vanilla JS

Hello everyone,

I’ve been trying to figure this out for a while now, but can’t get a decent result.
I hope someone can explain how I can achieve this.

Let’s take this jQuery example:


$("a[href$='.pdf']").prepend('<img src="images/pdf.png" alt="" />');

Now I would like to achieve the same result in vanilla JS. This is what I came up with, but as a JS newbie… I’m stuck:


function prepender(el, source, alt) {
	var anchor = document.querySelectorAll(el);
	var newElement = document.createElement("img");
	      newElement.src = source;
	      newElement.alt = alt;

	for (var i = 0; i < anchor.length; i++) {
		var arr = [];
		arr.push(anchor[i]);
		var parent = anchor[i].parentNode;
		parent.insertBefore(newElement, anchor[i]);
	}
}
prepender("a[href$='.pdf']", "images/pdf.png", "PDF link");	

Why am I only getting the last iteration?

I could be wrong, but it looks like you’re declaring arr as a blank array on each iteration. Move it to before the for loop… maybe that will fix it.

HTH,

:slight_smile:

Thanks for the effort WolfShade,
but moving the empty array up didn’t help:

function prepender(el, source, alt) {
	var anchor = document.querySelectorAll(el);
	var newElement = document.createElement("img");
	      newElement.src = source;
	      newElement.alt = alt;

	var arr = [];

	for (var i = 0; i < anchor.length; i++) {
		arr.push(anchor[i]);
		var parent = anchor[i].parentNode;
		parent.insertBefore(newElement, anchor[i]);
	}
}
prepender("a[href$='.pdf']", "images/pdf.png", "PDF link");

Somewhere in there, alert the length of anchor, just to make sure you’re getting more than the last one.

Curious: what is the purpose of the array? Why push data into it, but then use anchor[i] as the parent insert?

:slight_smile:

push is a command with which you can add an item to an existing array. In your case, a normal iteration should do:


function prepender(el, source, alt) {
    var anchor = document.querySelectorAll(el);
    var newElement = document.createElement("img");
        newElement.src = source;
        newElement.alt = alt;

    for (var i=0; i< anchor.length; i++) {
        var parent = anchor[i].parentNode;
        parent.insertBefore(newElement, anchor[i]);
    }
}
prepender("a[href$='.pdf']", "images/pdf.png", "PDF link");

Also… didn’t think of it, until now. Do all the PDF filenames end in “.pdf”? Or are there any that end in “.PDF”?

@Wolfshade,

I thought that I had to push/collect all the anchor tags containing the specified condition (in this case all anchor tags that have an attribute of src that ends with ‘.pdf’) in an empty array myself in order to work with them… but apparently not. Seems like the querySelectorAll() & for loop already take care of that if I’m understanding that correctly?

And the jQuery result will show two prepended PDF icons.

@Frank S,

Sure, please see attachment. Thanks for the effort!

I will get two results if I do a

console.log(anchor);

and that’s correct, so how and why is it skipping to the last result?

According to MDN, querySelectorAll() returns a list (not an array, as I mistakenly assumed) of all matching selectors. Maybe pushing the list into an array then looping the array?

HTH,

:slight_smile:

Hi,

Are you trying to write this function as an exercise?
If not, you can use:

parent.insertBefore(el, parent.firstChild);

Hi Pullo,

Yes it’s an exercise. It’s more important for me to know how and why something is done the way it’s done in pure/modern JS.

anchor.split(‘,’) should turn the list into an array without looping. Then you can loop the array and insertBefore().

:slight_smile:

Hi,

You can do it like this:

function prepender(el, source, alt) {
	var anchor = document.querySelectorAll(el);

	for (var i = 0; i < anchor.length; i++) {
	  var newElement = document.createElement("img");
	  newElement.src = source;
	  newElement.alt = alt;
	  
          var parent = anchor[i].parentNode;
	  parent.insertBefore(newElement, anchor[i].previousSibling);
	}
}
prepender("a[href$='.pdf']", "images/pdf.png", "PDF link");

The main difference being that the image creation has been moved into the for loop and you are inserting the new element not before the anchor tag, but before the anchor tag’s previous sibling.

A shorter way to do it would be:

var elems = document.querySelectorAll("a[href$='.pdf']");

[].forEach.call(elems, function(el, i) {
  var image = document.createElement("img");
  image.src = "https://d0.awsstatic.com/icons/file-icon-pdf-25x25.png";
  el.parentNode.insertBefore(image, el.previousSibling);
});

Demo

You might also want to read: http://toddmotto.com/a-comprehensive-dive-into-nodelists-arrays-converting-nodelists-and-understanding-the-dom/

@ Pullo,
I see…moving newElement into the for loop.

Thank you for helping me understand iteration in JS a little bit better and also thanks for the interesting resource.
Also thanks for showing me a shorter way with forEach! Very interesting and much appreciated. Keeps me motivated to do more with JS.

Cheers!
John