JavaScript JavaScript and the DOM Traversing the DOM Getting the First and Last Child

Linas Mackonis
Linas Mackonis
6,720 Points

Can anyone explain me this DOM traversal issue (Previous, Edit and Next buttons)

Hey all,

I'm trying to combine some JS features that we were learning in the following courses:

JavaScript and the DOM

DOM Scripting by Example

In the first course we have learned to implement Up and Down buttons, which navigates the <ul> in upward or downward directions. In the second course we learned to implement an Edit button.

However, I'm trying to implement all these features in one list item. Soon I encountered the behavior, which I cannot understand. Whenever I click a Previous button, the <li> moves to the left as expected, but <span> textContent disappears. Moreover, I end up having two Edit buttons. This behavior appears after I added this if.. else... code:

if(button.className === 'edit') {
            const span = li.firstElementChild;
            const input = document.createElement('input');
            input.type = 'text';
            input.value = span.textContent;
            li.insertBefore(input, span);
            li.removeChild(span);
            button.className = 'save';
            button.textContent = 'Save';
        } else {
            const input = li.firstElementChild;
            const span = document.createElement('span');
            span.textContent = input.value;
            li.insertBefore(span, input);
            li.removeChild(input);
            button.className = 'edit';
            button.textContent = 'Edit';
        }

Can somebody explain me what is happening here and why the whole <li> item, together with its content, can't move to the left?

My full code is bellow:

const form = document.querySelector('form');
const ul = document.querySelector('ul');
const listItem = document.querySelector('li');
const addItemInput = document.querySelector('input.add-item-input');
const addItemButton = document.querySelector('button.add-item-button');
const removeItemButton = document.querySelector('button.remove-item-button');

function attach_span(li) {
    const span = document.createElement('span');
    span.textContent = addItemInput.value;
    li.appendChild(span);
}

function attach_br(li) {
    const br = document.createElement('br');
    li.appendChild(br);
}

function attachButton_previous(li) {
    const prev = document.createElement('button');
    prev.className = 'previous';
    prev.textContent = 'Previous';
    if(li.previousElementSibling) li.appendChild(prev);
}

function attachButton_remove(li) {
    const remove = document.createElement('button');
    remove.className = 'remove';
    remove.textContent = 'Remove';
    li.appendChild(remove);
}

function attachButton_edit(li) {
    const edit = document.createElement('button');
    edit.className = 'edit';
    edit.textContent = 'Edit';
    li.appendChild(edit);
}

function attachButton_next(li) {
    const next = document.createElement('button');
    next.className = 'next';
    next.textContent = 'Next';
    if(li.nextElementSibling) li.appendChild(next);
}

function attachListItems(li) {
    attach_span(li);
    attach_br(li);
    attachButton_previous(li);
    attachButton_remove(li);
    attachButton_edit(li);
    attachButton_next(li);
}

addItemButton.addEventListener('click', () => {
    const li = document.createElement('li');
    ul.appendChild(li);
    attachListItems(li);
    addItemInput.value = '';
});

removeItemButton.addEventListener('click', () => {
    const ul = document.getElementsByTagName('ul')[0];
    const li = document.querySelector('li:last-child');
    ul.removeChild(li);
});

ul.addEventListener('click', (e) => {
    if(e.target.tagName === 'BUTTON' ) {
        const button = e.target;
        const li = button.parentNode;
        const ul = li.parentNode;
        const prevEl = li.previousElementSibling;
        const nextEl = li.nextElementSibling;
        if(button.className === 'remove') {
            ul.removeChild(li);
        }
        if(button.className === 'previous') {
            if(prevEl) {
                ul.insertBefore(li, prevEl);
            }
        }
        if(button.className === 'next') {
            if(nextEl) {
                ul.insertBefore(nextEl, li);
            }
        }
        if(button.className === 'edit') {
            const span = li.firstElementChild;
            const input = document.createElement('input');
            input.type = 'text';
            input.value = span.textContent;
            li.insertBefore(input, span);
            li.removeChild(span);
            button.className = 'save';
            button.textContent = 'Save';
        } else {
            const input = li.firstElementChild;
            const span = document.createElement('span');
            span.textContent = input.value;
            li.insertBefore(span, input);
            li.removeChild(input);
            button.className = 'edit';
            button.textContent = 'Edit';
        }
    }
});
Gari Merrifield
Gari Merrifield
9,548 Points

Can you add a demonstration HTML page for us? I am rather hands on, and like to see what is actually being worked with. The JS just doesn't make a lot of sense to me without the HTML that it is supposed to work with.

2 Answers

Linas Mackonis
Linas Mackonis
6,720 Points

I figured out, Hope this will be useful for anyone else. The problem can be fixed if these two conditionals:

if(button.className === 'edit') {
            const span = li.firstElementChild;
            const input = document.createElement('input');
            input.type = 'text';
            input.value = span.textContent;
            li.insertBefore(input, span);
            li.removeChild(span);
            button.className = 'save';
            button.textContent = 'Save';
        } else {
            const input = li.firstElementChild;
            const span = document.createElement('span');
            span.textContent = input.value;
            li.insertBefore(span, input);
            li.removeChild(input);
            button.className = 'edit';
            button.textContent = 'Edit';
        }

are moved above all the other conditionals in the corresponding event listener. Moreover, 'else' statement should be changed into 'else if'.

The final solution should look like this:

ul.addEventListener('click', (e) => {
    if(e.target.tagName === 'BUTTON' ) {
        const button = e.target;
        const li = button.parentNode;
        const ul = li.parentNode;
        const prevEl = li.previousElementSibling;
        const nextEl = li.nextElementSibling;
        if(button.className === 'edit') {
            const span = li.firstElementChild;
            const input = document.createElement('input');
            input.type = 'text';
            input.value = span.textContent;
            li.insertBefore(input, span);
            li.removeChild(span);
            button.className = 'save';
            button.textContent = 'Save';
        } else if(button.className === 'save') {
            const input = li.firstElementChild;
            const span = document.createElement('span');
            span.textContent = input.value;
            li.insertBefore(span, input);
            li.removeChild(input);
            button.className = 'edit';
            button.textContent = 'Edit';
        }

        if(button.className === 'remove') {
            ul.removeChild(li);
        }
        if(button.className === 'previous') {
            if(prevEl) {
                ul.insertBefore(li, prevEl);
            }
        }
        if(button.className === 'next') {
            if(nextEl) {
                ul.insertBefore(nextEl, li);
            }
        }
    }
});
Linas Mackonis
Linas Mackonis
6,720 Points

Yes sure. I have it pretty simple.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Testing JavaScript Events</title>
    <link rel="stylesheet" href="css/style.css">
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
</head>
<body>
    <header></header>
    <article>
        <nav id="main-nav">
            <ul>

            </ul>
        </nav>

        <form class="form">
            <input class="add-item-input" name="itemInput" type="text" title="Input for menu items">
            <button class="add-item-button" type="button">Add item</button>
            <button class="remove-item-button" type="button">Remove last item</button>
        </form>
</article>
    <p id="spitResult"></p>

    <!-- <script src="js/functions.js"></script> -->
    <script src="js/app.js"></script>
</body>
</html>
Gari Merrifield
Gari Merrifield
9,548 Points

Well, without your style sheet, they are stacked rather than side by side.

I am trying to understand a couple of things here : 1) what is the intended action if you click the "Previous" button 2) I see a "Next" button mentioned in your JS, but I don't see it showing up in my browser

The other buttons seem to work as their names would imply, Save, Edit, Remove, Add item, Remove last item

Are Previous and Next supposed to swap items around, or what is the intent?