HTML Collections to Arrays - I am stuck

Hello fellow coding friends, I hope you can help with a question that I’ve spent several hours on now and am stuck.

I have one list, where each list item has some data attributes that I hope to use for sorting into new lists ( ul) with some conditional logic. This is generated from a PHP query.

I converted the HTML collection into an array with Array.from(collection)

The problem I am trying to resolve is that when I filter by a data-attribute (it works fine) and create a new array, the items used to create the new array are removed from the original array and vanish from the DOM.

I’ve tried several approaches including .map, for loops newArray[i] = collectionArray[i], Array.reduce(). But all have the same result. I get a new array with the filtered items, but the items that were filtered are removed from the DOM. I even tried copying the arrays, same result.

So I’m guessing that all these steps are referencing the original HTML collection and not actually copying array items?

My question is, what’s going on(?!), and how would I take a list, convert to an array, apply filters by different data attributes and output to a different

    element by ID?

    Here’s a simple code example of what doesn’t work:

<!-- all the list elements output by php: using some goofy data attributes --> 
<ul id="all-js">

        <li data-actor="true" data-director="false">   Karen Gillan</li>

	<li  data-actor="true" data-director="false">   Carey Grant</li>

	<li  data-actor="false" data-director="true">   George Lucas</li>

</ul>

<ul id="actors-js">
	<h3> Active Actors</h3>
       <!-- use js to add active actors here -->
</ul>

<ul id="directors-js">
	<h3> Active Directors</h3>
       <!-- use js to add active directors here -->
</ul>

<script>

// Step 1: 
const htmlCollectionAll = document.getElementById('all-js').getElementsByTagName('li');

const everyoneArray = Array.from(htmlCollectionAll);

// trying it just with actors to show what I'm trying to do

actorsArray = everyoneArray.reduce((newArray, element) => {
	if(element.getAttribute('data-actor') === 'true'){
		newArray.push(element);
	}	
 
  return newArray;
}, []);

console.log(actorsArray);

//3. Add to DOM

const actorsSection = document.getElementById('actors-js');
actorsArray.forEach(addActorEntry);
	 
function addActorEntry(item) {
	let node = document.createElement("LI");
	let child = item;
	node.appendChild(child);
	console.log(node);
	actorsSection.appendChild(node);
}

</script>

At the end of your script, you’re trying to appendChild(node) to a variable called staffSection which is undefined. Did you mean to write actorsSection instead?

Yes, I renamed all the variables, array names for this post. I’ll fix that.

Oh I just noticed what your actual question was :smile:

The problem is that you’re taking the li elements from the collection and move them to the new section. To avoid that, you can make a clone of the item, so instead of let child = item:

let child = item.cloneNode(true);

1 Like

Woohoo! It works! Thanks so much.

So is the reason:

let child = item;

doesn’t work because that reassigns item to child within the function and thus removes the item from the array?

Do you think there’s a more concise way to achieve the goal I’m working on here or am I approaching it in a reasonable way?

Thanks again!

You’re literally moving the li element from the all-js list to the actors-js list. There’s only one item in your DOM, it can be either in the first or the second list but not in both, unless you clone it.

That very much depends on the whole setup, and how you get the data, and what functionality you want the page to have. You can do it all in JavaScript, so you’d have an array of objects with all people like this:

let staff = [
  {
    name:'Karen Gillan',
    actor: true,
    director: false
  },
  {
    name:'Carey Grant',
    actor: true,
    director:false
  },
  { ... }
]

I’d build my all-js list by iterating over all objects in the array and just appending li tags with the names - no data-attributes required.
Then I’d build my actors-js list from the same array, but this time I’d filter out the people where staffMember.actor === true. Same for the director-js list.

Alternatively, if you have access to the back end where the data is coming from, you could let PHP do the job, build the lists in a similar manner like described above, only in PHP, and serve the already-filtered lists.

If you only want to display the lists, I’d go for PHP. If you want to include some functionality like letting the user choose what data he wants to see, for example letting him do some cross-search like “show me all actors that are also directors” without the need to reload the page, I’d choose JavaScript.

Thanks so much for the detailed follow-up.

Using objects make total sense.

How this is actually working:

  • using custom post types with ACF in Wordpress

  • I was planning to output the whole list of people on the target div and then filter them with JS because I want to display them in a tabbed UI

  • Tabs would be: All | Actors | Directors | Locations (in this example)

  • I generated the list of people with WP_Query. I understand that I could do 2-3 different queries on the page, but my actual ACF fields also have sub fields (Location: Toronto, Los Angeles, New York, London, etc) that I would just separate in divs under Locations results. So I figured the WP_Query would get really messy. And I haven’t yet learned how to filter them with pre_get_posts() or any other method.

  • the most complication “result” would be that one person could belong to all of the following:

  • Several locations from the list
  • Actor
  • Director
  • I figured one WP_Query and then filter /display results based on Tab Button clicks with JS would be simplest and fastest.

  • I’m not planning on letting users build a query of their own like you mentioned, just a simple tabbed listing of those categories.

Thanks, again, you’ve given me lots to consider and helped me move ahead. @jsdisco

For the object, I’d need to build an object from the HTML collection if list elements, though, right?

Or perhaps I could get an array of people objects stored in a variable directly from the WP_Query to work with?

I’ve absolutely no experience with Wordpress, but yes - if you have a HTML collection with all the required data, you can take that (like you did above) and then manipulate other list items on your page depending on that data.

1 Like