Generate path on click from <ul> <li> listing using jQuery

Viewed 75

I have this listing with some files and folders:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css" />

<ul id="selector-file">
   <ul>
      <li class="folder">
         <i class="fa fa-folder"></i> myfiles
         <ul>
            <li class="folder">
               <i class="fa fa-folder"></i> Backup
               <ul>
                  <li class="folder">
                     <i class="fa fa-folder"></i> one
                     <ul>
                        <li class="file"><i class="fa fa-file"></i> test1.dat</li>
                     </ul>
                  </li>
                  <li class="folder"><i class="fa fa-folder"></i> two</li>
               </ul>
            </li>
         </ul>
      </li>
   </ul>
</ul>

This generates a tree structure:

enter image description here

Now, I want using jQuery, that when a item is clicked, return its path.

  • If I click, in myfiles output: /myfiles/
  • If I click, in one output: /myfiles/Backup/one/
  • If I click, in test.dat output: /myfiles/Backup/one/test1.dat

and so on.

PS: If its a folder, always add the / at the end.

This is my try:

    $('#selector-file').on('click', 'li', function() {  
        var item = $(this);
        var path = [];
        if (item.is('.file'))
        {
            $(this).css("background-color", "#F5F5F5")
            path.push(item.text().trim());
        } else {
            path.push(item.find('> .folder').text().trim() + '/');
        }
        var parents = item.parents('#selector-file li');
        path = parents.map(function() {
            return $(this).find('> .folder').text().trim();
        }).get().reverse().concat(path);
    
      selectedFile = path.join('/');
      console.log(selectedFile)
      return false;
})

... but it only returns the clicked item and no the path. Can someone give me a hand?

Here's a JSFiddle: https://jsfiddle.net/7n0h8y2t/

Thank you.

2 Answers

The Main problem was this:

$(this).find('> .folder')

Since $(this) refers to our li in the map then there would not be a direct child with the class folder

    $('#selector-file').on('click', 'li', function() {
      var item = $(this);
      var path = [];
      var AddSlash = true;
      if (item.is('.file')) {
        $(this).css("background-color", "#F5F5F5")
        AddSlash = false;
      }
      path.push(item.clone() //clone the element
        .children() //select all the children
        .remove() //remove all the children
        .end() //again go back to selected element
        .text().trim() + (AddSlash ? "/":""));
      var parents = item.parents('#selector-file li');

      path = parents.map(function() {
        return $(this).clone() //clone the element
          .children() //select all the children
          .remove() //remove all the children
          .end() //again go back to selected element
          .text().trim()
      }).get().reverse().concat(path);
      selectedFile = path.join('/');
      console.log(selectedFile)
      return false;
    })
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css" />

<ul id="selector-file">
  <ul>
    <li class="folder">
      <i class="fa fa-folder"></i> myfiles
      <ul>
        <li class="folder">
          <i class="fa fa-folder"></i> Backup
          <ul>
            <li class="folder">
              <i class="fa fa-folder"></i> one
              <ul>
                <li class="file">
                  <i class="fa fa-file"></i> test1.dat</li>
              </ul>
            </li>
            <li class="folder"><i class="fa fa-folder"></i> two</li>
          </ul>
        </li>
      </ul>
    </li>
  </ul>
</ul>

There's issues in the logic you use to build the path string, notably around selecting the .folder element which would not be a child of the li target you have.

In addition you should note that your JS code can be simplified massively by wrapping the folder/file name values in their own element, eg. a span, and then using parentsUntil() to retrieve them all. Try this:

$('#selector-file').on('click', 'li', e => {
  e.preventDefault();
  e.stopPropagation();
  let $li = $(e.currentTarget);
  let path = $li.parentsUntil('#selector-file', 'li').addBack().map((i, el) => el.querySelector('span').textContent.trim()).get().join('/');
  path += $li.hasClass('file') ? '' : '/';
  console.log(path);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css" />

<ul id="selector-file">
  <li class="folder">
    <i class="fa fa-folder"></i> 
    <span>myfiles</span>
    <ul>
      <li class="folder">
        <i class="fa fa-folder"></i> 
        <span>Backup</span>
        <ul>
          <li class="folder">
            <i class="fa fa-folder"></i> 
            <span>one</span>
            <ul>
              <li class="file">
                <i class="fa fa-file"></i> 
                <span>test1.dat</span>
              </li>
            </ul>
          </li>
          <li class="folder">
            <i class="fa fa-folder"></i> 
            <span>two</span>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

Related