How to check if conditions in loop are finished throught all items - and then do something in a loop?


#1

Cause the working example is really complicated lets suposse we just… testing page links (or any html element on a page). I simply don’t know how to check if ALL items meet the condition in the loop and if yes, perform an action. More in comments.

HTML:

<html>
<head>
<title>Links test</title>
</head>
<body>
	<!-- links are random - we do not know how many they are, and how many of them got title attribute -->
	<a href="https://www.google.com" title="google">google</a>
	<a href="https://facebook.com/">facebook<</a>
	<a href="https://www.instagram.com/" title="instagram">instagram</a>
	<a href="https://www.amazon.com/">amazon</a>
	<a href="https://www.apple.com/" title="apple">apple</a>
</body>
</html>

JS:

let links = document.getElementsByTagName('a');
let linksLength = links.length;
let titleCount = 0;

for (i = 0; i < linksLength; i++) { // main loop throught ALL links
	if (links[i].getAttribute('title') !== undefined) { // condition that check title attribute
		titleCount += 1; // this is cause I need to count all loop part that fulfil the condition
		// NOW if it's the last item that fulfil this condition then do something (in this main loop) - how to check it?
	}
}

#2

After the loop you might simply check

if (titleCount === links.length) {
  // Do something
}

A more elegant solution though would be to convert the node list to an array and then use the .every() method like so:

var allLinksHaveTitle = Array.from(links).every(function (link) {
  return link.title !== undefined
})

if (allLinksHaveTitle) {
  // Do something
}

#3

Be very careful here, guys. title is a special attribute for HTML elements - javascript (at least in Chrome) considers all links to have a title, and that it is the empty string if not specified, so undefined will never be true.

I would suggest a simpler method: Dont loop at all until you’re sure.
querySelectorAll to find all of the elements that have the attribute. Note that a CSS selector searching for an attribute (a[title]) will NOT find things without the attribute, regardless of Javascript’s default values.
If the length of the return = the length of the return from the getByTagName, you do your thing.


#4

Hi there spamator12,

here is a one possible solution…

<!DOCTYPE HTML>
<html lang="en">
<head>

<meta charset="utf-8">
<meta name="viewport" content="width=device-width,height=device-height,initial-scale=1">

<title>untitled document</title>

<!--<link rel="stylesheet" href="screen.css" media="screen">-->

<style media="screen">
body {
    background-color: #f9f9f9;
    font: 100% / 162% verdana, arial, helvetica, sans-serif;
 }

.highlight {
    color: #f00;
 }
</style>

</head>
<body>
 
 <ul>
  <li><a href="https://www.google.com" title="google">google</a></li>
  <li><a href="https://facebook.com/">facebook</a></li>
  <li><a href="https://www.instagram.com/" title="instagram">instagram</a></li>
  <li><a href="https://www.amazon.com/">amazon</a></li>
  <li><a href="https://www.apple.com/" title="apple">apple</a></li>
 </ul>

<script>
(function( d ) {
   'use strict';
   var c, count, link = d.getElementsByTagName( 'a' );
      for ( c = 0; c < link.length; c++ ) {
         if ( link[ c ].getAttribute( 'title' ) !== null ) {
              count = c;
         }
      }
      if ( count ) {
          doStuff( count );
      }

function doStuff( count ) {
   link[ count ].classList.add( 'highlight' );
 }
}( document ));
</script>

</body>
</html>

coothead


#5

@ m3g4p0p close one. I did use every and some other method (filter, etc.) to target the items that do not have the specific marks/attributes.

The thing is I need to do this in a loop. It’s hard to explain (I have edited the question with HTML variable).

@ m_hutley

Be very careful here, guys. title is a special attribute for HTML elements - javascript (at least in Chrome) considers all links to have a title, and that it is the empty string if not specified, so undefined will never be true.

Got it. Didn’t know of this behavior, thanks! I have edited the question.

I would suggest a simpler method: Don’t loop at all until you’re sure.

Yea but then we are looping twice - once to get all element that does not meet some standard, and again through all elements. I need to use this loop cause it have some async operations later on, and they need to target the data (added HTML variable in this example) that this loop passed in a specific order.

If the length of the return = the length of the return from the getByTagName, you do your thing.

This is exactly what I did.

@ coothead nice. But there can more links without title then one. If

      if ( count ) {
          doStuff( count );
      }

would be putted inside for loop, then it would work I suppose, if I understand this correctly of course.

EDIT: Maybe I will just post code here (did add many console.log to see running results):

						let links = document.getElementsByTagName('a');
						console.log('======================');
						console.log('links HTML collection:\n');
						console.log(links);
						console.log('======================');
						let linksLength = links.length;
						let linksTitleCount = 0;
						let html = '';


						let linksArray = Array.from(links); // converting HTML collection to an array
						
						let allLinksThatDoesHaveTitleAttr = linksArray.filter(function(l) 
						{
							return (l.getAttribute('title') !== undefined && l.getAttribute('title') !== null && l.getAttribute('title') !== '');
						});
						let allLinksThatDoesHaveTitleAttrCount = allLinksThatDoesHaveTitleAttr.length;
						console.log();
						console.log('======================');
						console.log('allLinksThatDoesHaveTitleAttribute:\n');
						console.log(allLinksThatDoesHaveTitleAttrCount);
						console.log('======================');


						for (i = 0; i < linksLength; i++) { // main loop throught ALL links
							// lets supose we... build html element
							html += 'link' + [i] +'\n';

							if (links[i].getAttribute('title') !== undefined && links[i].getAttribute('title') !== null && links[i].getAttribute('title') !== '') { // condition that check existance of title attribute - thanks @m_hutley
								linksTitleCount += 1; // this is cause I need to count all loop part that fulfil the condition
								console.log(linksTitleCount);
								console.log();
								
								html += 'this link - ' + [i] + ' - does not have title attribute! \n\n';
								
								if (linksTitleCount == allLinksThatDoesHaveTitleAttrCount)
								{
									console.log('DONE!'); // we know we are done - we loop throught all elements that does have title attr
									// NOW if it's the last item that fulfil this condition then do something (in this main loop) + some async operations that will leater (when data arrive) target specific HTML with specific link (order like in loop - [i])
								}
							}
						}
						
						console.log('HTML result is:\n');
						console.log(html);

#6

Your request was …

NOW if it’s the last item that fulfil this condition then do something

…and that Is what I coded for. :winky:

Have you now changed your mind?

coothead


#7

Yep you are corrects. The question at first was like in first post. I was just think about something like in “answer” :slight_smile:


#8

Why not just use the Array.prototype.every() method, to test if every link meets the condition that you require?

const hasTitle = (link) => link.getAttribute("title") > "";
function doLinkAction(link) {
    ...
}
...
if (links.every(hasTitle)) {
    links.forEach(doLinkAction);
}