htmlajax

HTML Details tag load only specific content that is clicked


I am using the wonderful details tag as follows, which expands the content when a link is clicked:

summary::marker {
  content: " ";
}

summary {
  color: blue;
  text-decoration: underline;
  cursor: pointer;

}
<details>
  <summary>Show Details</summary>
  <h2>HTML Details</h2>
  <p>Explanation of the topic</p>
</details>

This tag allows for including all kinds of html tags, including images to be included in the expanded details.

For example, the above code will first show the following:

enter image description here

and when the "Show Details" link is clicked, the HTML will be expanded for that particular topic: enter image description here

Note that in the expanded form, the term "Pythagoras Theorem" is in bold, as I used the b tag.

It works well.

But I have run into a situation where the single page contains around 300 such topics, and "Show Details" for each of those includes around 3 image files each.

That means, I will have around 900 images loaded on that webpage as soon as someone visits it. Not good as it will load slow, and the visitor may only click on expanding 1-2 topics. So loading all 900 images is not needed.

I was looking for customization such that when a particular topic's Show Details link is clicked, only that particular HTML is loaded and displayed. Clubbing AJAX/JS is a solution, but AJAX has a limitation that it does not close the earlier expanded one automatically.

Something like combining the details tag and AJAX, so that expanding one should show that particular image, and close the other image. (details tag allows for only 1 clicked element to be displayed, and automatically closes the earlier clicked one).


Solution

  • Well, finally, here's the simplest idea: use a <template>.

    Then retrieve the toggle event of the detail element (checking the open state) to clone the contents of the template, which loads the image(s). ;)

    see template doc -> https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/template

    Sample code :

    document.querySelectorAll('details').forEach( elmD =>
      {
      elmD.addEventListener('toggle', e =>
        {
        let tempElm = elmD.querySelector('template');
        if (!tempElm || !elmD.open) return;
        elmD.append(document.importNode(tempElm.content, true));
        tempElm.remove(); // doesn't needed anymore...
        }) 
      })
    html {
      font-family : Arial, Helvetica, sans-serif;
      font-size   : 16px;
      }
    
    summary::marker {
      content : '';
      }
    summary {
      color           : blue;
      text-decoration : underline;
      cursor          : pointer;
      }
    <details>
      <summary>Show Details #1</summary>
      <h3> info (h3) </h3>
      <template>
        <p>
          Find this...<br>
          <img src="https://picsum.photos/id/21/200/120" alt=" alt for img a">
        </p>
        <p>
          To walk the streets...<br>
          <img src="https://picsum.photos/id/22/200/120" alt=" alt for img b">
        </p>
      </template>
    </details>
    
    <details>
      <summary>Show Details #2</summary>
      <h3> info (h3) </h3>
      <template>
        <p>
          Wash and put away<br>
          <img src="https://picsum.photos/id/23/200/120" alt="alt for img a">
        </p>
        <p>
          To avoid damage on :<br>
          <img src="https://picsum.photos/id/24/200/120" alt="alt for img b">
        </p>
      </template>
    </details>
    
    <details>
      <summary>Show Details #3</summary>
      <h3> info (h3) </h3>
      <template>
        <p>
          In case of a wild expedition<br>
          <img src="https://picsum.photos/id/25/200/120" alt=" alt for img a">
        </p>
        <p>
          Prepare for anything !<br>
          <img src="https://picsum.photos/id/26/200/120" alt=" alt for img b">
        </p>
      </template>
    </details>

    Additionally, with a cleanup of becoming useless Event Listener :

    document.querySelectorAll('details:has(template)').forEach( elmD =>
      {
      elmD.addEventListener('toggle', fctToggleDetails);
      })
    
    function fctToggleDetails(e)
      {
      if (!this.open)
        return
        
      let tempElm = this.querySelector('template');
      this.append(document.importNode(tempElm.content, true));
      this.removeEventListener('toggle', fctToggleDetails);
      tempElm.remove();  // doesn't needed anymore...
      }
    html {
      font-family : Arial, Helvetica, sans-serif;
      font-size   : 16px;
      }
    
    summary::marker {
      content : '';
      }
    summary {
      color           : blue;
      text-decoration : underline;
      cursor          : pointer;
      }
    <details>
      <summary>Show Details #1</summary>
      <h3> info (h3) </h3>
      <template>
        <p>
          Find this...<br>
          <img src="https://picsum.photos/id/21/200/120" alt=" alt for img a">
        </p>
        <p>
          To walk the streets...<br>
          <img src="https://picsum.photos/id/22/200/120" alt=" alt for img b">
        </p>
      </template>
    </details>
    
    <details>
      <summary>Show Details #2</summary>
      <h3> info (h3) </h3>
      <template>
        <p>
          Wash and put away<br>
          <img src="https://picsum.photos/id/23/200/120" alt="alt for img a">
        </p>
        <p>
          To avoid damage on :<br>
          <img src="https://picsum.photos/id/24/200/120" alt="alt for img b">
        </p>
      </template>
    </details>
    
    <details>
      <summary>Show Details #3</summary>
      <h3> info (h3) </h3>
      <template>
        <p>
          In case of a wild expedition<br>
          <img src="https://picsum.photos/id/25/200/120" alt=" alt for img a">
        </p>
        <p>
          Prepare for anything !<br>
          <img src="https://picsum.photos/id/26/200/120" alt=" alt for img b">
        </p>
      </template>
    </details>