Making sure script runs on one element

Conceptual question. I have a project where a page will have a number of full-width elements each containing unique text. Within each element, is optional button to reveal extra unique text. The button/reveal/toggle I have is working. I want to be sure that when someone clicks, the script only runs inside the full-width element containing the button & text. I’m guessing that I can either A) Tie the onclick somehow to its adjacent elements, or B) Put the contents of ea full-width element inside a unique ID, that will be passed to the script. (Asking because better to start with a clear idea.)
Here’s a codepen
Ignore text where communicating with client.

I see you have the aria-controls attribute on the button, which points to the id of the text you are toggling. I think that is your answer right there. Each button will have an aria-controls attribute pointing to the id of the text it toggles. The click handler for the button can just look at the value of that attribute to determine which element to toggle.

Also, the aria-expanded attribute goes only on the button, not on the element that is being toggled.

Bruce, I will step through the code this week looking for that specific behavior. Thanks for this tip I had not yet considered.

(reply to an email) Apologies for that. Codepen offers a “collab” mode if I fork over some $$$$$$
I thought folks in this forum would just look through the source.

For the record, replit.com offers a free shared editing mode. I mean, they have a subscription service, but the pair-programming doesn’t require payment.

1 Like

First, if you’re going to do multiple elements, there are a few considerations you need to change:

  • Don’t use ids unless absolutely necessary. They are required to be unique, and in this case, that’s going to cause problems. Instead, if you need an easy reference, use classes.
  • Structure your HTML so that each “full-width element containing unique text…and extra text” are contained as discrete units. Something like this:
<article>
  <section>
    <p class="show-hide hidetext">Here is some new, added 'disclosed' content. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor <a href="#">incididunt ut labore</a> et dolore magna aliqua. </p>
    <button type="button">
      <span>
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80" focusable="false">
          <path d="M70.3 13.8L40 66.3 9.7 13.8z"></path>
        </svg>
      </span><span class='button-text'>READ MORE...</span>
    </button>
    <p class="near-button-txt"><strong>First Full-width element.</strong> Curabitur aliquet quam id dui posuere blandit. Quisque velit nisi, pretium ut lacinia in, elementum id enim.<br><a href="#">Product 123 details</a></p> 
 </section>
  <section>
    Similar content to above. Each <strong>section</strong> tag contains another full-width element, complete in itself.
  </section>
</article>

Now, with that HTML, and with the following javascript:

function handleButtonClick(event){
  // what should happen in here?
}

const buttons = document.querySelectorAll("section button")

buttons.forEach(function(button){
  button.addEventListener("click", handleButtonClick); 
}

Your homework, perhaps, would be to write out in your own words, without code, what you expect to happen when one of the buttons is clicked.

  • How can you find which button was clicked?
  • Can you find a way to get from that to which grouping container that button lives in?
  • Given that, can you see a way to get the particular hidden text you want?
  • With all that handled, can you think of a way to toggle that particular text from hidden to displayed?
  • Say I click on the “READ MORE…” in the third button, should we be hiding all the other ones and just show that one?

Writing a spec, or building to a spec, is an important part of coding. And writing out your plan in plain language is a key bridge from an idea to an action.

1 Like

I completely agree with this sentiment but in this case you have to use an id on the text element that is being toggled because the aria-controls attribute can only point to an id.

:man_shrugging: In that case, it would seem you have one of two choices:

  • stick with aria-controls and manually code each button to tie it to a toggle element’s id, or
  • look at a different approach than aria-controls.

The fact is, to make it happen, you will need the javascript to toggle both the normal javascript-y things, and the aria-* values. When the Read More is first clicked, you’ll need to get its relevant controlled element, then maybe do the following:

- If the `aria-expanded` for the current button is "true":
  - set it to "false",
  - set the `aria-hidden` for the `aria-control`-id'ed element to "true",
  - and likely add some CSS class controlling the hidden state to it.
- else:
  - set the `aria-expanded` for the button to "true",
  - set the `aria-hidden` for the `aria-controls`-id'ed element to "false",
  - and remove the CSS class controlling the hidden state.

Is that the logic you’d use for this, @bbsmooth ? I’m trying to grok, as I haven’t really played much with aria-*.

An accessible disclosure widget like this should use aria-controls so there really is no alternative here. You don’t necessarily need to use aria-hidden here because you can hide the text by toggling the CSS display property (block/none), unless for some reason display: none won’t work for you. (I’m going to assume for now that it will work).

Thus, the click handler needs to do two things:

  • Toggle a class on the element that is being disclosed to control the display property
  • Update aria-expanded on the button

The two states are:

  • display: none on text and aria-pressed="false" on button
  • display: block on text and `aria-pressed=“true” on button

Since the button also has the aria-controls attribute pointing to the id of the element it is toggling, you might as well just use that id to toggle the class on the element.

1 Like

I’m taking notes on all replies. Today I was hammered by everything except this project. Tomorrow should be better…

Bruce, when my client first requested this, he pointed out that Google won’t collect SEO on display:none text. This is why I’m using the height zero-to-full in the CSS. Otherwise, I am completely on your plan.

If you can’t use display: none then you’ll have to add the aria-hidden attribute on the text that is being toggled, setting it to “true” when it is hidden and “false” when not.

Yipes. One more reason I tell you guys what reckless things I’m doing.

Unless your client first requested this pre-2017… Modern SEO is very different. I found the following quote or things like it on multiple search results for “SEO and display:none”:

A long time ago Matt Cutts from Google said that they devaluate the content hidden in tabs. However after the Google Mobile First Indexing took place in 2019 this has changed.

So, now if you have a tab or anything that is hidden by display:none and can be shown to the visitor via a JS it is no problem. Google can understand that this content is hidden to make the page more user-friendly and does count the hidden content.

However if you plan on hiding completely a snippet from a page using the display:none function in CSS it is not the best approach, but still it won’t hurt your rankings. Just this content won’t be count when Google algorithm ranks your website.

So long as you have a display mechanism, Google recognizes that you’re not maliciously hiding content.

Edit: an article about this very topic, citing Google interviews. Google's Mueller on Indexing of Hidden Tab Content

1 Like

I’m so glad I have a job that doesn’t worry about SEO :slight_smile:

I believe you. But client is noticing I’m a week late on delivery. Once I have a clean version in his hands, I can run the page through some SEO tools and hopefully prove this viewpoint.

I have the clean version running on a repl, i just figured you won’t learn if i simply give it to you lol

1 Like

I installed a couple of JS extensions in my VS Code, and also signed up as OkieFrontend at repl. Onward, upward.

1 Like

New version replaces all but one ID per previous comment about use in ARIA. I fixed two of three problems in new JS. I’m stuck on two things. 1-Try in to extract the third piece of a node ( theAriAtt = NodeList(2) [p#toggletext_0.hidetext, p#toggletext_1.hidetext] ) and then utilize that piece in an IF statement, and 2-Making sure the node-parsing IF logic is within the unique ID where the user clicked.
My function fncShowHideTxt() is choking on (theAriAtt.getAttribute("class") === "hidetext")

The error msg is “Uncaught TypeError: theAriAtt.getAttribute is not a function”

Here is the full function…

function fncShowHideTxt() {
  var theAriAtt = document.querySelectorAll('[id^="toggletext_"]');
  console.log(theAriAtt);
if (theAriAtt.getAttribute("class") === "hidetext") {
    theAriAtt.setAttribute("class", "showtext");
    theAriAtt.setAttribute("aria-hidden", "false");
    } else {
        theAriAtt.setAttribute("class", "hidetext");
        theAriAtt.setAttribute("aria-hidden", "true");
  };
};

Here is my new repl