Are there any caveats to populating an accordion via a database?

I am using the simple accordion here:
https://www.w3schools.com/howto/howto_js_accordion.asp

I am able to use TaffyDB to populate the drawers, but the drawers don’t open when clicked. (The output html for the drawer is correct in the console.log.) The drawer button and name that you click on successfully is successfully output from TaffyDB, but the drawer itself won’t open.

Just wondering if there is a gotcha with this approach. Not sure what code to show, since it works, but doesn’t work!

The order of the javascript, relative to the html, is probably the cause, but this is just a guess. Since you have a page that doesn’t work, you would need to post all the code needed to reproduce the problem to get help with what is actually wrong.

Here it is:
http://www.reedyrace.com/ae/cheatsheets/cheatsheets/general.html

mabismad is on the right track. Your code in taffyDB-general.js happens AFTER you select the accordion elements in cheatsheets.js. Meaning at the time the code in cheatsheets.js is run, the taffyDB accordion element isn’t populated yet. So it binds the click event to the other items because those are specified directly in the HTML.

My suggestion is to move taffyDB-general.js up above cheatsheets.js and I think that might fix it for you. Let us know how it goes.

Not sure that’ll work, or at best might be a race condition?

cheatsheets is being called inline, function is delayed until onload.

This is really a case of event delegation, or else at the very least an application of the listener to the new row. or including the code from cheatsheets into the database loader so that it fires after the rows have been added.

Moving the taffy files immediately above cheatsheets.js or into the head doesn’t change the outcome. Drawer does not open.

Put the css at the botttom of the js file? Interesting thought. Never did that before.

I did it! I added the first part of the cheatsheets.js to the top of the repeating section:

var acc = document.getElementsByClassName("accordion");
var i;

… and the rest of the code to the bottom of the repeating section, and it worked. It now opens and shows the contents of the drawer. I left the css file where it was.

Unfortunately (later edit), it won’t show more entries that are added to the DB, only the last one.

sigh… Ok let me explain guys. Your cheatsheets.js is adding event listeners. It does this by selecting the accordion elements and then looping through them to attach a listener to each one.

Since your taffyDB-general.js script is taking some time, the code in cheatsheets.js is executing BEFORE the code in taffyDB-general is finished adding the top accordion. So it never gets a click event.

Don’t believe me? Wrap your for loop in a setTimeout function with a delay.

setTimeout(function() {
	for (i = 0; i < acc.length; i++) {
	  
	  acc[i].addEventListener("click", function() {
	    this.classList.toggle("active");
	    var panel = this.nextElementSibling;
	    if (panel.style.maxHeight) {
	      panel.style.maxHeight = null;
	    } else {
	      panel.style.maxHeight = panel.scrollHeight + "px";
	    }
	  });

	}
}, 1000);

Here we are saying, go through the loop after waiting a second (aka 1000 milliseconds). This gives the code in taffyDB-general.js to finish populating the top accordion node. At which time it can then attach event listeners to ALL the elements.

I was hoping just moving the script would suffice, but it appears that taffyDB-general may take more time than that. So either you can leave this setTimeout in place, or make sure that the code in taffyDB is finished before looping through the elements to add listeners.

But hey, if you got it working by moving some random code around, all the power to you. I thought I would explain what is really going on here.

Good luck to you. :slight_smile:

1 Like

Take the entirity of cheatsheets.js.

Stick it here.

		console.log("output = " + copy1 + copy2 + copy3 + copy4);
	 });
   #HERE.
 }

Read as: Execute the event listener binds after all of the elements are in place.

Adding the timeout did now work. It prevented the drawer from even opening. Strange.

When I put a console.log here, why does it output 0, 1, 2, 3 when there are only 2 records in the DB? Is it counting the 3 hard-coded “accordion” buttons on the page?

/* cheatsheets.js START */
	var acc = document.getElementsByClassName("accordion");
	var i;
	for (i = 0; i < acc.length; i++) {
console.log("var i = " + i);
	  acc[i].addEventListener("click", function() {
	    this.classList.toggle("active");
	    var panel = this.nextElementSibling;
	    if (panel.style.maxHeight) {
	      panel.style.maxHeight = null;
	    } else {
	      panel.style.maxHeight = panel.scrollHeight + "px";
	    }
	  });
	}
/* cheatsheets.js END */

A small change here (+= copy1) makes all records display:

output.innerHTML+= copy1 + copy2 + copy3 + copy4;

This project is finished! I updated the top link to show completed code working.

If there are 3 accordions, then it’s starting at 0, and will loop through until i is not less than acc.length, which you mentioned is 3 accordions. So it will loop through with i being 0, then 1, then 2. It stops there because if i = 3, then 3 is not less than 3.