Bug when importing from a file? (Added Video)

Video here.

This is a bit hard to explain without showing, but I will do the best that I can…

Why is it when I store a variable with document.querySelector element in a file then call it in another causing bugs but when I call document.querySelector directly instead of importing works just fine?

For anyone curious, I’m working on a challenge for a Udemy course project called Forkify. I also ask the question more detailed here.

I’ll appreciate any help.

Maybe you forgot to export the variable?

P.S. That Udemy link is not accessible for anyone who hasn’t bought the course.

There are plenty of other calls to direct to that file so it has nothing to do with import/export. Adding the ingredients to the shopping list works fine the first time but then when delete all shopping list items then click the button again, it doesn’t work anymore. But using direct call works seamlessly. Truly hard to explain unless I take a video of it I guess. :sweat_smile: And that’s lame you can’t check my Udemy question.

See if this makes any sense to you (they’re mere snippets.)
listView.js

import { elements } from './base';
 
export const renderItem = item => {
    const markup = `
                <li class="shopping__item" data-itemid=${item.id}>
                    <div class="shopping__count">
                        <input type="number" value="${item.count}" step="1" class="shopping__count-value">
                        <p>${item.unit}</p>
                    </div>
                    <p class="shopping__description">${item.ingredient}</p>
                    <button class="shopping__delete btn-tiny">
                        <svg>
                            <use href="img/icons.svg#icon-circle-with-cross"></use>
                        </svg>
                    </button>
                </li>
    `;
    //elements.shoppingList.insertAdjacentHTML('beforeend', markup);
    document.querySelector('.shopping__list').insertAdjacentHTML('beforeend', markup);
}
 
export const deleteItem = id => {
    const item = document.querySelector(`[data-itemid="${id}"]`);
    if (item) item.parentElement.removeChild(item);
}
 
export const deleteAll = () => {
    /*elements.shoppingList.remove();
    /elements.shoppingListContainer.insertAdjacentHTML('afterbegin', `
        <ul class="shopping__list"></ul>
    `);*/
    document.querySelector('.shopping__list').remove();
    document.querySelector('.shopping__list-container').insertAdjacentHTML('afterbegin', `
        <ul class="shopping__list"></ul>
    `);
}

index.js

//LIST CONTROLLER
const controlList = () => {
    //CREATE NEW LIST IF NONE EXISTING
    if (!state.list) state.list = new List();
    //ADD EACH INGREDIENT TO THE LIST & UI
    state.recipe.ingredients.forEach(elem => {
        const item = state.list.addItem(elem.count, elem.unit, elem.ingredient);
        listView.renderItem(item);
    })
};
 
//HANDLE DELETE AND UPDATE LIST ITEM EVENTS
elements.shoppingList.addEventListener('click', event => {
    const id = event.target.closest('.shopping__item').dataset.itemid;
    //HANDLE DELETE BUTTON
    if (event.target.matches('.shopping__delete, .shopping__delete *')) {
        //DELETE FROM STATE
        state.list.deleteItem(id);
        //DELETE FROM UI
        listView.deleteItem(id);
    } else if (event.target.matches('.shopping__count-value')) {
        const value = parseFloat(event.target.value, 10);
        state.list.updateCount(id, value);
    }
});
 
//CHALLENGE
elements.shopping.addEventListener('click', event => {
    if (!state.list) state.list = new List();
    if (state.list.items.length > 0) if (event.target.matches('.shopping__delete-all, .shopping__delete-all *')) {
    state.list.deleteAll();
    listView.deleteAll();
    console.log('clicked');
    }
});

base.js

export const elements = {
    searchForm: document.querySelector('.search'),
    searchInput: document.querySelector('.search__field'),
    searchResult: document.querySelector('.results'),
    searchResultsList: document.querySelector('.results__list'),
    searchResultsPages: document.querySelector('.results__pages'),
    recipe: document.querySelector('.recipe'),
    shopping: document.querySelector('.shopping'),
    shoppingList: document.querySelector('.shopping__list'),
    shoppingListContainer: document.querySelector('.shopping__list-container'),
    likesMenu: document.querySelector('.likes__field'),
    likesList: document.querySelector('.likes__list')
};

index.html

<div class="shopping">
            <h2 class="heading-2">My Shopping List</h2>
            
            <div class ="shopping__list-container">
            <ul class="shopping__list">
 
                <!--
                <li class="shopping__item">
                    <div class="shopping__count">
                        <input type="number" value="500" step="100">
                        <p>g</p>
                    </div>
                    <p class="shopping__description">Pasta</p>
                    <button class="shopping__delete btn-tiny">
                        <svg>
                            <use href="img/icons.svg#icon-circle-with-cross"></use>
                        </svg>
                    </button>
                </li>
 
                <li class="shopping__item">
                    <div class="shopping__count">
                        <input type="number" value="0.5" step="0.1">
                        <p>cup</p>
                    </div>
                    <p class="shopping__description">Ricotta cheese</p>
                    <button class="shopping__delete btn-tiny">
                        <svg>
                            <use href="img/icons.svg#icon-circle-with-cross"></use>
                        </svg>
                    </button>
                </li>
 
                <li class="shopping__item">
                    <div class="shopping__count">
                        <input type="number" value="3.5" step="0.1">
                        <p>tbsp</p>
                    </div>
                    <p class="shopping__description">Toasted almond slices</p>
                    <button class="shopping__delete btn-tiny">
                        <svg>
                            <use href="img/icons.svg#icon-circle-with-cross"></use>
                        </svg>
                    </button>
                </li>
 
                <li class="shopping__item">
                    <div class="shopping__count">
                        <input type="number" value="0.5" step="0.1">
                        <p>tbsp</p>
                    </div>
                    <p class="shopping__description">Sea salt</p>
                    <button class="shopping__delete btn-tiny">
                        <svg>
                            <use href="img/icons.svg#icon-circle-with-cross"></use>
                        </svg>
                    </button>
                </li>
 
                <li class="shopping__item">
                    <div class="shopping__count">
                        <input type="number" value="0.25" step="0.1">
                        <p>cup</p>
                    </div>
 
                    <p class="shopping__description">Minced green onions</p>
                    <button class="shopping__delete btn-tiny">
                        <svg>
                            <use href="img/icons.svg#icon-circle-with-cross"></use>
                        </svg>
                    </button>
                </li>
 
                <li class="shopping__item">
                    <div class="shopping__count">
                        <input type="number" value="45" step="10">
                        <p>g</p>
                    </div>
                    <p class="shopping__description">Sesame seeds</p>
                    <button class="shopping__delete btn-tiny">
                        <svg>
                            <use href="img/icons.svg#icon-circle-with-cross"></use>
                        </svg>
                    </button>
                </li>
                -->
            </ul>
            </div>
 
            <div class="shopping__delete-all">
                <a class="btn-small recipe__btn" target="_blank">
                    <svg class="search__icon">
                        <use href="img/icons.svg#icon-circle-with-cross"></use>
                    </svg>
                    <span>Delete All</span>
                </a>
            </div>
 
            <div class="copyright">
                &copy; by Jonas Schmedtmann. Powered by
                <a href="http://food2fork.com" target="_blank" class="link">Food2Fork.com</a>.
            </div>
 
        </div>

I added a video. Please check when you can. Thanks!:crazy_face:

Better post your code to something like codesandbox.io

Hmm… I tried, but it doesn’t look like it should. :persevere: I don’t want to publicize my API for food2fork anyway. I will run out of usage. I only have the free one and can only make 50 calls a day per account and I’m already using 2 accounts. Lol. And I need each usage that I can so I can keep practicing.

When you initially define a selector, it looks up into the DOM, finds the element, and stores the reference of it. And then never looks up again if you use the variable name like so:

// looks up the element and caches/saves it's reference into the variable
const shoppingList = document.querySelector('.shoppingList');

// Now whenever you use `shoppingList` variable, it will give you it's reference, instead of looking up again

And if the element gets deleted, or the parent element’s innerHTML changes, it’s reference is also lost.

And, one more thing. the elements objects that has the selectors, MUST run after the page loads, because if you access any HTML element before declaring/loading HTML, you will get null… Here’s what I mean: https://codepen.io/anon/pen/rPYZWB

If you’re not overriding the selectors with innerHTML, or by any other means, the problem would likely be because you’re declaring them before the DOM nodes actually exists. (Or if they are being created dynamically)

1 Like

You shouldn’t be using limited APIs for practicing anyway. Instead take the response from that API call, save it locally and use that local file.

1 Like

Wow. Thanks for the amazing explanation. The deletion and recreation of the ul did cross my mind actually. I’ll try using remove child like the teacher does instead of just remove. Will definitely need to keep practicing to figure out this whole execution stack thing. Thank you!

Alsp, I love your icon. :heart_eyes:

1 Like

Neat idea. Will have to google how to do that. Thanks!