Javascript cards arranged in descending order

I'm in the process of developing a sorting feature that arranges cards in the DOM from Z to A with the click of a button. Although I've successfully implemented the logic for sorting the array, I'm facing difficulties in rendering it.

Within my project, I have a Drink Class and a DrinkCard Class where the DrinkCard handles card creation and the Drink manages drinks.

I believe calling the Drink class might assist in rendering the sorted array on the DOM, but I'm unsure of the approach to take. Feeling stuck at the moment.

This is the progress so far: UPDATE Following the suggestion below, I made updates, but rendered-content id isn't available anywhere. Instead, I used querySelector on the class .card, leading to the current error message displayed below.

Uncaught DOMException: Failed to execute 'appendChild' on 'Node': The new child element contains the parent.
    at Drink.render (file:///Users/austinredmond/dev/caffeine_me/frontend/src/models/drink.js:28:17)
    at file:///Users/austinredmond/dev/caffeine_me/frontend/src/index.js:43:38
    at Array.forEach (<anonymous>)
    at HTMLInputElement.<anonymous> (file:///Users/austinredmond/dev/caffeine_me/frontend/src/index.js:43:17)
render @ drink.js:28
(anonymous) @ index.js:43
(anonymous) @ index.js:43
sortDesc.addEventListener("click", () => {
   const sortedArray = allDrinks.sort((a, b) => {
        const nameA = a.name.toLowerCase(),
            nameB = b.name.toLowerCase()
        if (nameA < nameB) //sort string ascending
            return 1
        if (nameA > nameB)
            return -1
        return 0 //default return value (no sorting)
    })

    const node = document.querySelector('.card');
    sortedArray.forEach(card => card.render(node));

})

Drink Class

class Drink {

    constructor(data) {
        // Assign Attributes //
        this.id = data.id
        this.name = data.name
        this.caffeine = data.caffeine
        this.comments = []

        this.card = new DrinkCard(this, this.comments)

    }

    // Searches allDrinks Array and finds drink by id //
    static findById(id) {
        return allDrinks.find(drink => drink.id === id)
    }

    // Delete function to Delete from API //
    delete = () => {
        api.deleteDrink(this.id)
        delete this
    }

    render(element) {
        // this method will render each card; el is a reference to a DOM node
        console.log(element)
        element.appendChild(this.card.cardContent);

    }
}

DrinkCard Class

class DrinkCard {
    constructor(drink, comments) {
        // Create Card //
        const card = document.createElement('div')
        card.setAttribute("class", "card w-50")
        main.append(card)
        card.className = 'card'

        // Add Nameplate //
        const drinkTag = document.createElement('h3')
        drinkTag.innerText = drink.name
        card.append(drinkTag)

        // Add CaffeinePlate //
        const caffeineTag = document.createElement('p')
        caffeineTag.innerText = `Caffeine Amount - ${drink.caffeine}`
        card.append(caffeineTag)

        // Adds Create Comment Input Field //
        const commentInput = document.createElement("input");
        commentInput.setAttribute("type", "text");
        commentInput.setAttribute("class", "input-group mb-3")
        commentInput.setAttribute("id", `commentInput-${drink.id}`)
        commentInput.setAttribute("placeholder", "Enter A Comment")
        card.append(commentInput);

        // Adds Create Comment Button //
        const addCommentButton = document.createElement('button')
        addCommentButton.innerText = "Add Comment"
        addCommentButton.setAttribute("class", "btn btn-primary btn-sm")
        card.append(addCommentButton)
        addCommentButton.addEventListener("click", () => this.handleAddComment())

        // Add Comment List //
        this.commentList = document.createElement('ul')
        card.append(this.commentList)

        comments.forEach(comment => this.addCommentLi(comment))

        // Create Delete Drink Button
        const addDeleteButton = document.createElement('button')
        addDeleteButton.setAttribute("class", "btn btn-danger btn-sm")
        addDeleteButton.innerText = 'Delete Drink'
        card.append(addDeleteButton)
        addDeleteButton.addEventListener("click", () => this.handleDeleteDrink(drink, card))

        // Connects to Drink //
        this.drink = drink

        this.cardContent = card;
    }

    // Helpers //

    addCommentLi = comment => {
        // Create Li //
        const li = document.createElement('li')
        this.commentList.append(li)
        li.innerText = `${comment.summary}`

        // Create Delete Button
        const button = document.createElement('button')
        button.setAttribute("class", "btn btn-link btn-sm")
        button.innerText = 'Delete'
        li.append(button)
        button.addEventListener("click", () => this.handleDeleteComment(comment, li))
    }

    // Event Handlers //
    // Handle Adding Comment to the DOM //
    handleAddComment = () => {
        const commentInput = document.getElementById(`commentInput-${this.drink.id}`)
        api.addComment(this.drink.id, commentInput.value)
            .then(comment => {
                commentInput.value = ""
                const newComment = new Comment(comment)
                Drink.findById(newComment.drinkId).comments.push(newComment)
                this.addCommentLi(newComment)
            })
    }

    // Loads last comment created for drink object //
    handleLoadComment = () => {
        const newComment = this.drink.comments[this.drink.comments.length - 1]
        this.addCommentLi(newComment)
    }

    // Deletes Comment from API and Removes li //
    handleDeleteComment = (comment, li) => {
        comment.delete()
        li.remove()
    }

    // Deletes Drink from API and Removes drink card //
    handleDeleteDrink = (drink, card) => {
        drink.delete()
        card.remove()
    }
}

Answer №1

If you're looking to display Drink cards on your website, here are a couple of methods you can consider:

  1. Pass a reference to a DOM node where the cards should be appended
  2. Extract the raw HTML from the card and insert it into an element when needed

To implement method (1), make the following changes:

Update DrinkCard.js as follows:

class DrinkCard {
    constructor(drink, comments) {
         // Your existing code
         this.cardContent = card; // or card.innerHTML
    }
}

Update Drink.js as follows:

class Drink {
   // Your existing code

   render(el) {
      // This method will render each card; el is a reference to a DOM node
      el.appendChild(this.card.cardContent);

   }
}

Lastly, pass the DOM reference to the sorted entries:

const node = document.getElementById('rendered-content'); // Ensure this element exists

sortedArray.forEach(card => card.render(node));

This approach should assist you in rendering the cards according to your requirements.

Updates

The error message you received may have arisen due to the following reasons:

  1. Firstly, the element with id rendered-content might not exist in your DOM
  2. Using .card to append the rendered element could lead to a cyclical error by attempting to add an element (.card) to itself

To address this issue, you can try the following steps:

  1. Include
    <div id="rendered-content"></div>
    somewhere in your HTML where the sorted cards should be displayed
  2. If you prefer not to have it hardcoded in the HTML, create it dynamically before referencing it, like so:
const rc = document.createElement('div');
rc.setAttribute('id', 'rendered-content');
document.body.appendChild(rc);

const node = document.getElementById('rendered-content');
sortedArray.forEach(card => card.render(node));

These adjustments should help mitigate the errors you encountered.

Further Explanation

A brief overview of browser rendering and its operation in this scenario will be provided, along with a link to a more comprehensive article for additional insights.

In the rendering process, the following stages occur:

  1. The browser retrieves and parses your HTML document
  2. A DOM tree is constructed based on the parsed document
  3. CSS styling is applied to the DOM elements
  4. The content of the DOM is painted onto the screen

Your initial code handled most aspects adequately. You created the cards, added their content, and sorted them based on the Drink type. The only missing step was integrating this into the DOM.

When dynamically generating elements as done in the DrinkCard class, it's crucial to attach them to the existing DOM for visibility. Triggering modifications to the DOM prompts layout adjustment and repainting, ultimately displaying your content on the screen.

The purpose of the div with id='rendered-content' is to act as a container within your DOM or be generated prior to usage. When inserting nodes into the DOM, a valid reference element is required. While document.body could serve as a default reference, using a dedicated container grants more flexibility in presentation options.

For a detailed exploration of browser rendering processes, refer to this informative discussion here. Trust this explanation clarifies any uncertainties you may have.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Having difficulty accessing the value of a table td element in HTML using a jQuery selector

When creating a table, I utilize ng-repeat to generate table rows. Whenever the dropdown changes, a function is triggered which applies certain conditions. Based on these conditions, an object is added to an array that is bound to a scope variable. Here i ...

Looking to clean up unnecessary <b></b> tags from my Wordpress website - how can I do this?

One of my sites is located at . When inspecting the source code through the browser, I noticed the presence of empty tags. Does anyone know why these empty tags are being generated and how they can be removed? I have thoroughly searched through all files ...

"I'm encountering an issue with the discord.js module when I try to launch my bot using node. Any ideas on how

I encountered an unusual error with my Discord bot recently. It seems that discord.js crashes every time I try to run my bot: [nodemon] 2.0.12 [nodemon] to restart at any time, enter `rs` [nodemon] watching path(s): *.* [nodemon] watching extensions: js,mj ...

Prisma data is not being returned as an array in getServerProps with React Next.js

My Journey with Next.js and Prisma Having recently grasped the concept of getServerProps, I embarked on a project that involved retrieving data from a PostgreSQL database using Prisma. However, despite diligently following the syntax rules outlined in the ...

Using both Ajax and a standard submit can be applied to a single form in jQuery

I need to implement a confirm step on one of my pages. Here's what I want to achieve: When the user clicks 'submit', an AJAX request is triggered, which performs the necessary action and then displays a confirmation dialog If the user ...

Having trouble with Angular JS $scope.$apply due to an interpolation error displaying "TypeError: Converting circular structure to JSON"?

I have created a specialized angular directive shown below: dirs.directive('sectionInfo', function(){ return { restrict: 'E', templateUrl: 'partials/section-home.html', transclude: true, co ...

Automated pagination integrated with automatic refreshing of div every few seconds

I am currently working on setting up a datatable and I need it to be displayed on the monitor in such a way that it can refresh the div and automatically paginate to the next page. However, whenever the div is refreshed, the auto-pagination gets cancelle ...

Convert the numerical values from an array into an input field format

Currently, I have two inputs and an array with two number positions. The v-model in each input corresponds to a value in the array. Whenever a change is made in either input field, it reflects on the corresponding position in the array, which works perfect ...

Transforming a form containing recurring elements into JSON

Sorry if this topic has already been discussed, I wasn't able to locate any information I currently have an html form structured like so: <form> <div> First Name <input name="FirstName" type="text"> Age <input name="Age" t ...

Utilizing AngularJS to iterate through an array of dictionaries

Within a specific section of my HTML code, I am initializing a scope variable like this: $scope.my_data = [ { c1: "r1c1", c2: "r1c2", c3: "r1c3", ...

Retrieve: Type 'string | undefined' does not match the parameter type 'RequestInfo'

When using the fetch function, I encountered an error with the "fetchUrl" argument: Error: Argument of type 'string | undefined' is not assignable to parameter of type 'RequestInfo'. This is the code snippet where the error occurred: ...

Guide on inserting text within a Toggle Switch Component using React

Is there a way to insert text inside a Switch component in ReactJS? Specifically, I'm looking to add the text EN and PT within the Switch Component. I opted not to use any libraries for this. Instead, I crafted the component solely using CSS to achie ...

Creating a clickable button within an image container using Bootstrap 5

I am attempting to create a button inside an img element, specifically in the center of that img element within Bootstrap 5. The image is not being used as a background on the grid and I am applying some hover animations to zoom in. I am curious if there ...

Using Angular to automatically update the user interface by reflecting changes made in the child component back to the parent component

Within Angular 5, I am utilizing an *IF-else statement to determine if the authorization value is true. If it is true, then template 2 should be rendered; if false, then template 1 should be rendered. Below is the code snippet: <div *ngIf="authorized; ...

Is there a way for me to retrieve the information within this function? It seems like it may be a promise, but I am uncertain

I need help extracting the data from doc.data and sending it to another component based on the dropdown selection. It appears that the data is in promise format, and despite trying async/await, I am struggling to access it. Can anyone guide me on how to ...

Instantly refreshing the Angular DOM following data modifications and retrieval from the database

I am currently working on a Single Page Application that uses Angular 8 for the frontend and Laravel for the backend. This application is a CRUD (Create, Read, Update, Delete) system. The delete functionality is working as expected, deleting users with spe ...

Using jQuery, set a restriction on the total number of options in three dropdown menus to

I am currently facing a challenge with 3 dropdowns, each containing 8 options. My aim is to restrict the total selection across all three dropdowns to 8. For instance, if I choose 7 in the first dropdown, I should only be able to pick 1 in the next two. Si ...

Angular - The argument provided is not compatible with the parameter

I encountered the following TypeScript errors in app.component.ts: Issue: Argument of type '(events: Event[]) => void' is not assignable to parameter of type '(value: Event[]) => void'. Description: Types of parameters 'e ...

Need to battle with... its own contradictions in a direct manner

I've integrated APIs for my bot, expecting them to work smoothly. However, I'm still struggling to figure out why the server keeps aborting itself... Here is the error output, code, and a screenshot. Any help would be greatly appreciated. Error: ...

Copy the style of a chosen element and apply it to another element

One of the challenges I'm facing involves a dynamic span element that changes based on color selection: <span class="color checked" style="background-color: #ff0000">Red</span> In addition, I have an image element wit ...