javascripthtmlnodesparent-child

Fetching of HTML li text content


I am working on an AI project, and I need to gather the contents of the li using JavaScript. There can be multiple of these li's

My HTML (just a small section)

<div>
    <button id="roomListButton">Click me to see what rooms your in</button>
        <ul id="roomsList">

        </ul>
            
</div>

My code (not everything)

const roomsListR = document.querySelector('#roomsList');

const delRoomButTEMPLATE = document.createElement('button')
delRoomButTEMPLATE.classList.add('deleteRoomBtn');
delRoomButTEMPLATE.innerHTML = `Leave this room`;

const fetchRoomsAndProcess = async (event) => {
    console.log('fetching rooms')
    event.preventDefault();

    const response = await fetch('/getRooms');
    const jsonResponse = await response.json();
    const result = jsonResponse.result

    // Preparatory clean of the rooms list
    roomsListR.innerHTML = ``
    
    for (let i = 5; i != 0; i--) {
        let currentRoom = result.splice(0, 1)
        let currentRoom2 = currentRoom[0]
        let currentRoom3 = currentRoom2['roomname']
        console.log('currentRoom3 : ', currentRoom3);

        // Now do the stuff with currentRoom
        const li = document.createElement('li');
        li.innerHTML = `${currentRoom3} ${delRoomButTEMPLATE.outerHTML}`

        li.addEventListener('click', (event) => {
            console.log('button clicked.')
            const parent = event.target.parentNode
            console.log(parent.innerHTML)
        })

        roomsListR.appendChild(li)

        if (result.length == 0) {
            break
        }
    }
}

The syntax: li.textContent does not work as it returns the text content of the button element inside it as well. li.innerText for some reason does the same thing. And li.innerHTML includes the <button> tag which is not what I want.

Here's what the website looks like: a picture of the website

Is there a shortcut for retrieving just the text in the node, not including any tags or anything inside of those tags?


Solution

  • This answer addresses multiple points in the shown code, not just the "how can I retrieve the room name from the HTML" question.


    Cleanup

    The for-loop seems to exist so that you can .splice the rooms from the const result binding. But why do you "iterate" backwards (= count down) but then extract a single item from the front? All in all, wouldn't it be much clearer to just use:

    for (const room of result) {
      // ... elided
    

    and be done? This change would turn

    let currentRoom = result.splice(0, 1)
    let currentRoom2 = currentRoom[0]
    let currentRoom3 = currentRoom2['roomname']
    

    into

    let currentRoom = room.roomname;
    

    because you would not have to "unwrap" the object that holds the .roomname property from "leftover" arrays. Plus, it'd make the "termination" if-statement unneeded:

    if (result.length == 0) {
      break;
    }
    

    because the loop automatically stops when all rooms have been iterated.

    If you don't like the for-of suggestion, you could also do:

    for (let i = 0; i < result.length; i++) {
      let currentRoom = result[i].roomname;
    
      // ... elided
    

    Closures to the rescue!

    One way or another, even if you do not change the loop or let-bindings: You already have the room's name when the <li> is created:

    const li = document.createElement('li');
    li.innerHTML = `${currentRoom3} ${delRoomButTEMPLATE.outerHTML}; // <-- currentRoom3
    

    This means you can use it as a closure when attaching the event listener:

    li.addEventListener('click', () => {
      console.log('clicked room:', currentRoom3); // <-- currentRoom3
    });
    

    No need to "read"/"extract"/"parse" it from the HTML.


    Final

    All in all, here's how the code looks if you employ all suggestions:

    const fetchRoomsAndProcess = async (event) => {
      console.log('fetching rooms')
      event.preventDefault();
    
      const response = await fetch('/getRooms');
      const jsonResponse = await response.json();
      const result = jsonResponse.result
    
      // Preparatory clean of the rooms list
      roomsListR.innerHTML = ``
        
      for (const room of result) {
        let currentRoom = room.roomname;
    
        // Now do the stuff with currentRoom
        const li = document.createElement('li');
        li.innerHTML = `${currentRoom} ${delRoomButTEMPLATE.outerHTML}`
    
        li.addEventListener('click', () => {
          console.log('clicked room:', currentRoom);
        });
    
        roomsListR.appendChild(li);
      }
    }
    

    If that does not work out of the box because all clicks log the same value for currentRoom, you have to wrap the event handler function with an iife:

    li.addEventListener('click', ((name) => {
      return () => {
        console.log('clicked room:', name);
      };
    })(currentRoom));
    

    Of course, you can shorted it into:

    li.addEventListener('click', ((name) => () => {
      console.log('clicked room:', name);
    })(currentRoom));
    

    But IMHO the longer version is easier to grasp. However, YMMV.