1

I tried to set it up when pressing a "a" key to open in order, but it opens all at the same time.

Expected outcome:

  1. "a" key = open Details1
  2. "a" key = open Details2
  3. "a" key = open Details3

document.onkeyup = function(e) {
    var e = e || window.event;
    if (e.which == 65) {
        $('details').attr('open', true);
    }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<details>
    <summary>Details1</summary>
    test1
    
    <details>
        <summary>Details2</summary>
    test2
    
        <details>
            <summary>Details3</summary>
    test3

        </details>
    </details>
</details>
Toto
  • 89,455
  • 62
  • 89
  • 125
Huandney
  • 23
  • 4

2 Answers2

2

this way
For information, the details elements work with a toggle event
and their open attribute type is Boolean.

For more explanation please referto my answer there

// array of details elements in hierarchical order
const details_tree = [...document.querySelectorAll('body>details, body>details>details, body>details>details>details')] 

document.onkeydown=e=>
  {
  if (e.key==='a')                   // open part 
  for (let D of details_tree)       // loop to find  
  if (!D.open)                     // the first closed (not open)
    {
    D.open = true                 // open it
    break                        // break the loop
    }
  if (e.key==='b')                       // close part
  for (let i=details_tree.length;i--;)  // reverse loop to find  
  if (details_tree[i].open)            // the last open 
    {
    details_tree[i].open = false      // close it
    break                            // break the loop 
    }
  }
 
// option : when closing a <detail> element, sub <details> will be closed too
details_tree.forEach((D,index,A)=>{
  D.ontoggle =_=>{ if(!D.open) A.forEach((d,i)=>{ if(i>index) d.open=false })}
})
details {
  margin  : .5em;
  border  : 1px solid lightgrey;
  padding : .4em;
}
<details>
  <summary>Details1</summary>
  test1
  <details>
    <summary>Details2</summary>
    test2
    <details>
      <summary>Details3</summary>
      test3
    </details>
  </details>
</details>
Mister Jojo
  • 20,093
  • 6
  • 21
  • 40
2

JQuery returns collections of elements matching a query, and calling a method on a collection performs the method on each member - the reason they all open at once.

  • To open details one at time you can iterate through the collection and open the first one that is not open.
  • To close the last one opened you can create an array from the details collection, reverse the array, create a new collection from the array, find the find the first opened element in the reversed collection and close it (phew).
  • To close all opened elements, simply remove the open attribute from the entire details collection.

Note that open is an attribute without a value - the details element is open if the attribute is present and closed if it is absent.

Unfortunately jQuery doesn't have a "hasAttribute" function, and while work-arounds have been devised, many of them do not apply to attributes without a value. However the native hasAttribute element method has been around longer than the <details> element and is supported in all modern browsers.

So using jQuery (mostly) you could try the following. Type 'a' to open a single element, 'c' to close the last open element, or 'z' to close all open (details) elements:

document.onkeyup = function(e) {
    var e = e || window.event;
    if (e.which == 65) {        // 'a' - open one
        $('details').each( function() {
            if( this.hasAttribute("open")) {
                return true;
            }
            $(this).attr("open", true);
            return false;   
        });
    }
    else if(e.which == 67) {    // 'c' - close one
       $($('details').get().reverse()).each( function() {
            if( this.hasAttribute("open")) {
                $(this).removeAttr("open");
                return false;
            }  
        });
    }
    else if(e.which == 90) {    // 'z' - close all
        $('details').removeAttr('open');
    }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<details>
    <summary>Details1</summary>
    test1
    
    <details>
        <summary>Details2</summary>
    test2
    
        <details>
            <summary>Details3</summary>
    test3

        </details>
    </details>
</details>
traktor
  • 17,588
  • 4
  • 32
  • 53
  • And out of curiosity, how would it look to close them? sorry for my questions, i'm just learning to program... – Huandney Mar 29 '21 at 12:17
  • 1
    I've updated the post with an example of closing all `details` elements or just the last one opened - and also found out that `each` calls its argument function with `this` set to the element value for the iteration, which can simplifiy the code. I find the philosophy of jQuery quite different to plain JavaScript and leave it up to you to decide which you want to focus on while learning. Do check out [jQuery documentation](https://api.jquery.com/) if you take it further :) – traktor Mar 29 '21 at 15:13