created a function and a HTML structure to populate the DOM from a JSON object
This commit is contained in:
		
							parent
							
								
									4941ce5aaf
								
							
						
					
					
						commit
						d14988146b
					
				
					 1 changed files with 258 additions and 43 deletions
				
			
		| 
						 | 
					@ -37,6 +37,7 @@
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        table#view_table thead th {
 | 
					        table#view_table thead th {
 | 
				
			||||||
            text-align: center;
 | 
					            text-align: center;
 | 
				
			||||||
 | 
					            vertical-align: bottom;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        table#view_table thead th:first-child {
 | 
					        table#view_table thead th:first-child {
 | 
				
			||||||
            font-weight: bold;
 | 
					            font-weight: bold;
 | 
				
			||||||
| 
						 | 
					@ -44,6 +45,14 @@
 | 
				
			||||||
        table#view_table thead th.me {
 | 
					        table#view_table thead th.me {
 | 
				
			||||||
            font-weight: bold;
 | 
					            font-weight: bold;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        table#view_table thead th img {
 | 
				
			||||||
 | 
					            display: block;
 | 
				
			||||||
 | 
					            margin: auto;
 | 
				
			||||||
 | 
					            margin-bottom: 1em;
 | 
				
			||||||
 | 
					            border-radius: 50%;
 | 
				
			||||||
 | 
					            max-width: 4em;
 | 
				
			||||||
 | 
					            max-height: 4em;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        table#view_table tbody tr {
 | 
					        table#view_table tbody tr {
 | 
				
			||||||
            border-bottom: 1px solid #dddddd;
 | 
					            border-bottom: 1px solid #dddddd;
 | 
				
			||||||
| 
						 | 
					@ -55,6 +64,8 @@
 | 
				
			||||||
        table#view_table tbody th span {
 | 
					        table#view_table tbody th span {
 | 
				
			||||||
            font-size: 0.8em;
 | 
					            font-size: 0.8em;
 | 
				
			||||||
            font-weight: normal;
 | 
					            font-weight: normal;
 | 
				
			||||||
 | 
					            display: inline-block;
 | 
				
			||||||
 | 
					            margin-left: 0.33em;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        table#view_table tbody tr:nth-of-type(odd) {
 | 
					        table#view_table tbody tr:nth-of-type(odd) {
 | 
				
			||||||
            background-color: var(--colour_lightgrey);
 | 
					            background-color: var(--colour_lightgrey);
 | 
				
			||||||
| 
						 | 
					@ -113,68 +124,272 @@
 | 
				
			||||||
<body>
 | 
					<body>
 | 
				
			||||||
    <table id="view_table">
 | 
					    <table id="view_table">
 | 
				
			||||||
        <thead>
 | 
					        <thead>
 | 
				
			||||||
            <tr>
 | 
					            <tr id="view_table_thead_headers">
 | 
				
			||||||
                <th>
 | 
					                <th id="view_table_thead_name"></th>
 | 
				
			||||||
                    <!-- Group name -->
 | 
					 | 
				
			||||||
                    Les abeilles en test
 | 
					 | 
				
			||||||
                </th>
 | 
					 | 
				
			||||||
                <!-- For-each group member -->
 | 
					                <!-- For-each group member -->
 | 
				
			||||||
                <th>Alice</th>
 | 
					 | 
				
			||||||
                <th class="me">Moi (Loulou)</th>
 | 
					 | 
				
			||||||
                <th>Bob</th>
 | 
					 | 
				
			||||||
            </tr>
 | 
					            </tr>
 | 
				
			||||||
        </thead>
 | 
					        </thead>
 | 
				
			||||||
        <tbody>
 | 
					        <tbody>
 | 
				
			||||||
            <!-- For-each date -->
 | 
					            <!-- For-each date -->
 | 
				
			||||||
            <tr>
 | 
					 | 
				
			||||||
                <th>
 | 
					 | 
				
			||||||
                    Date #1
 | 
					 | 
				
			||||||
                    <br>
 | 
					 | 
				
			||||||
                    <span title="1🏠 + 1🚅">🇸🇪 2</span>
 | 
					 | 
				
			||||||
                    <span title="1🏠">🗼 1</span>
 | 
					 | 
				
			||||||
                </th>
 | 
					 | 
				
			||||||
                <!-- For-each group member -->
 | 
					 | 
				
			||||||
                <td>
 | 
					 | 
				
			||||||
                    <span class="where">
 | 
					 | 
				
			||||||
                        🗼
 | 
					 | 
				
			||||||
                    </span>
 | 
					 | 
				
			||||||
                </td>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <td class="me">
 | 
					 | 
				
			||||||
                    <span class="where">
 | 
					 | 
				
			||||||
                        🇸🇪
 | 
					 | 
				
			||||||
                        <i class="modifier modifier-bottom modifier-cross">🚅</i>
 | 
					 | 
				
			||||||
                    </span>
 | 
					 | 
				
			||||||
                </td>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <td>
 | 
					 | 
				
			||||||
                    <span class="where">
 | 
					 | 
				
			||||||
                        🥨
 | 
					 | 
				
			||||||
                        <i class="modifier modifier-top modifier-cross">🏠</i>
 | 
					 | 
				
			||||||
                        <i class="modifier modifier-bottom modifier-cross">🚅</i>
 | 
					 | 
				
			||||||
                    </span>
 | 
					 | 
				
			||||||
                </td>
 | 
					 | 
				
			||||||
            </tr>
 | 
					 | 
				
			||||||
        </tbody>
 | 
					        </tbody>
 | 
				
			||||||
        <tfoot>
 | 
					        <tfoot>
 | 
				
			||||||
            <tr>
 | 
					            <tr>
 | 
				
			||||||
                <th>
 | 
					                <th id="view_table_tfoot_total"></th>
 | 
				
			||||||
                    <!-- Total -->
 | 
					 | 
				
			||||||
                    3 group members
 | 
					 | 
				
			||||||
                </th>
 | 
					 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                <!-- Colspan = number of members -->
 | 
					                <!-- Colspan = number of members -->
 | 
				
			||||||
                <th colspan="3"></th>
 | 
					                <th id="view_table_tfoot_filler"></th>
 | 
				
			||||||
            </tr>
 | 
					            </tr>
 | 
				
			||||||
        </tfoot>
 | 
					        </tfoot>
 | 
				
			||||||
    </table>
 | 
					    </table>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <p>
 | 
					    <p>
 | 
				
			||||||
        So far, this group has planned between 2022-08-01 and 2024-11-25.
 | 
					        So far, this group has planned between
 | 
				
			||||||
 | 
					        <span id="view_first_filled_date"></span>
 | 
				
			||||||
 | 
					        and
 | 
				
			||||||
 | 
					        <span id="view_last_filled_date"></span>.
 | 
				
			||||||
    </p>
 | 
					    </p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <p>
 | 
					    <p>
 | 
				
			||||||
        <a href="index.html">Take me home!</a>
 | 
					        <a href="index.html">Take me home!</a>
 | 
				
			||||||
    </p>
 | 
					    </p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <template id="row_date">
 | 
				
			||||||
 | 
					        <tr>
 | 
				
			||||||
 | 
					            <th></th>
 | 
				
			||||||
 | 
					            <!-- For-each group member = availability -->
 | 
				
			||||||
 | 
					        </tr>
 | 
				
			||||||
 | 
					    </template>
 | 
				
			||||||
 | 
					    <template id="cell_availability">
 | 
				
			||||||
 | 
					        <td>
 | 
				
			||||||
 | 
					            <span class="where"></span>
 | 
				
			||||||
 | 
					        </td>
 | 
				
			||||||
 | 
					    </template>
 | 
				
			||||||
 | 
					    <template id="modifier_cannot_host">
 | 
				
			||||||
 | 
					        <i class="modifier modifier-top modifier-cross" title="Cannot host">🏠</i>
 | 
				
			||||||
 | 
					    </template>
 | 
				
			||||||
 | 
					    <template id="modifier_cannot_travel">
 | 
				
			||||||
 | 
					        <i class="modifier modifier-bottom modifier-cross" title="Cannot travel">🚅</i>
 | 
				
			||||||
 | 
					    </template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <script>
 | 
				
			||||||
 | 
					        var userUid = 1;
 | 
				
			||||||
 | 
					        var viewData = {
 | 
				
			||||||
 | 
					            view: {
 | 
				
			||||||
 | 
					                name: "Le groupe des abeilles",
 | 
				
			||||||
 | 
					                numberOfGroupMembers: 3,
 | 
				
			||||||
 | 
					                firstFilledDate: "2023-05-06",
 | 
				
			||||||
 | 
					                lastFilledDate: "2025-01-31"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            members: [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    uid: 1,
 | 
				
			||||||
 | 
					                    displayName: "Louis",
 | 
				
			||||||
 | 
					                    avatar: "https://static.david-david-studio.com/image/24511.jpg"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    uid: 2,
 | 
				
			||||||
 | 
					                    displayName: "Mathilde",
 | 
				
			||||||
 | 
					                    avatar: "https://tinder.com/static/tinder.png"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    uid: 3,
 | 
				
			||||||
 | 
					                    displayName: "Louis",
 | 
				
			||||||
 | 
					                    avatar: "https://www.aprifel.com/wp-content/uploads/2019/02/potiron.jpg"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            dates: [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    date: "2024-08-01",
 | 
				
			||||||
 | 
					                    membersAvailability: [
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            memberUid: 1,
 | 
				
			||||||
 | 
					                            location: "🇸🇪",
 | 
				
			||||||
 | 
					                            canHost: true,
 | 
				
			||||||
 | 
					                            canTravel: true
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            memberUid: 2,
 | 
				
			||||||
 | 
					                            location: "🗼",
 | 
				
			||||||
 | 
					                            canHost: true,
 | 
				
			||||||
 | 
					                            canTravel: true
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            memberUid: 3,
 | 
				
			||||||
 | 
					                            location: "🥨",
 | 
				
			||||||
 | 
					                            canHost: true,
 | 
				
			||||||
 | 
					                            canTravel: true
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    ]
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    date: "2024-08-02",
 | 
				
			||||||
 | 
					                    membersAvailability: [
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            memberUid: 1,
 | 
				
			||||||
 | 
					                            location: "🗼",
 | 
				
			||||||
 | 
					                            canHost: false,
 | 
				
			||||||
 | 
					                            canTravel: true
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            memberUid: 2,
 | 
				
			||||||
 | 
					                            location: "🗼",
 | 
				
			||||||
 | 
					                            canHost: true,
 | 
				
			||||||
 | 
					                            canTravel: true
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            memberUid: 3,
 | 
				
			||||||
 | 
					                            location: "🥨",
 | 
				
			||||||
 | 
					                            canHost: true,
 | 
				
			||||||
 | 
					                            canTravel: false
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    ]
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        function data2dom(data) {
 | 
				
			||||||
 | 
					            // Retrieve common DOM elements
 | 
				
			||||||
 | 
					            var headerRow = document.getElementById("view_table_thead_headers");
 | 
				
			||||||
 | 
					            var tbody = document.getElementById("view_table").querySelector("tbody");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Prepared data
 | 
				
			||||||
 | 
					            var numberOfDisplayedMembers = data.members.length;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Static: update header
 | 
				
			||||||
 | 
					            document.getElementById("view_table_thead_name").textContent = data.view.name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Static: update footer
 | 
				
			||||||
 | 
					            let strViewMembersCount = data.view.numberOfGroupMembers + " group member";
 | 
				
			||||||
 | 
					            if(data.view.numberOfGroupMembers > 1) strViewMembersCount += "s";
 | 
				
			||||||
 | 
					            document.getElementById("view_table_tfoot_total").textContent = strViewMembersCount;
 | 
				
			||||||
 | 
					            document.getElementById("view_table_tfoot_filler").setAttribute("colspan", numberOfDisplayedMembers);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Static: update below table
 | 
				
			||||||
 | 
					            document.getElementById("view_first_filled_date").textContent = data.view.firstFilledDate;
 | 
				
			||||||
 | 
					            document.getElementById("view_last_filled_date").textContent = data.view.lastFilledDate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // For each member
 | 
				
			||||||
 | 
					            data.members.forEach(element => {
 | 
				
			||||||
 | 
					                let newCell = document.createElement("th");
 | 
				
			||||||
 | 
					                let displayName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if(element.uid == userUid) {
 | 
				
			||||||
 | 
					                    displayName = "Me (" + element.displayName + ")";
 | 
				
			||||||
 | 
					                    newCell.classList.add("me");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else {
 | 
				
			||||||
 | 
					                    displayName = element.displayName;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if(element.avatar) {
 | 
				
			||||||
 | 
					                    let newAvatar = document.createElement("img");
 | 
				
			||||||
 | 
					                    newAvatar.setAttribute("src", element.avatar);
 | 
				
			||||||
 | 
					                    newAvatar.setAttribute("alt", "Photo for " + element.displayName);
 | 
				
			||||||
 | 
					                    newCell.appendChild(newAvatar);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                let newTextData = document.createTextNode(displayName);
 | 
				
			||||||
 | 
					                newCell.appendChild(newTextData);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                headerRow.appendChild(newCell);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            // For each date
 | 
				
			||||||
 | 
					            const rowDateTemplate = document.getElementById("row_date");
 | 
				
			||||||
 | 
					            const cellAvailabilityTemplate = document.getElementById("cell_availability");
 | 
				
			||||||
 | 
					            const modifierCannotHostTemplate = document.getElementById("modifier_cannot_host");
 | 
				
			||||||
 | 
					            const modifierCannotTravelTemplate = document.getElementById("modifier_cannot_travel");
 | 
				
			||||||
 | 
					            data.dates.forEach(element => {
 | 
				
			||||||
 | 
					                let clonedRow = rowDateTemplate.content.cloneNode(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let header = clonedRow.querySelector("th");
 | 
				
			||||||
 | 
					                let headerName = document.createTextNode(element.date);
 | 
				
			||||||
 | 
					                header.appendChild(headerName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let locations = [];
 | 
				
			||||||
 | 
					                element.membersAvailability.forEach(availability => {
 | 
				
			||||||
 | 
					                    // Creating the DOM
 | 
				
			||||||
 | 
					                    let clonedCell = cellAvailabilityTemplate.content.cloneNode(true);
 | 
				
			||||||
 | 
					                    if(userUid == availability.memberUid) {
 | 
				
			||||||
 | 
					                        clonedCell.children[0].classList.add("me");
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    let whereSpan = clonedCell.querySelector("span.where");
 | 
				
			||||||
 | 
					                    let locationText = document.createTextNode(availability.location);
 | 
				
			||||||
 | 
					                    whereSpan.appendChild(locationText);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if(!availability.canHost) {
 | 
				
			||||||
 | 
					                        let clonedModifier = modifierCannotHostTemplate.content.cloneNode(true);
 | 
				
			||||||
 | 
					                        whereSpan.appendChild(clonedModifier);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    if(!availability.canTravel) {
 | 
				
			||||||
 | 
					                        let clonedModifier = modifierCannotTravelTemplate.content.cloneNode(true);
 | 
				
			||||||
 | 
					                        whereSpan.appendChild(clonedModifier);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    clonedRow.children[0].appendChild(clonedCell);
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    // Aggregating data per location, for totals
 | 
				
			||||||
 | 
					                    let existingLocations = locations.filter(e => e.location === availability.location);
 | 
				
			||||||
 | 
					                    if(existingLocations.length < 1) {
 | 
				
			||||||
 | 
					                        locations.push({
 | 
				
			||||||
 | 
					                            location: availability.location,
 | 
				
			||||||
 | 
					                            membersAvailable: 0,
 | 
				
			||||||
 | 
					                            hostsAvailable: 0,
 | 
				
			||||||
 | 
					                            travellersAvailable: 0
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Update other locations if member can travel
 | 
				
			||||||
 | 
					                element.membersAvailability.forEach(availability => {
 | 
				
			||||||
 | 
					                    locations.forEach(existingLocation => {
 | 
				
			||||||
 | 
					                        if(existingLocation.location == availability.location) {
 | 
				
			||||||
 | 
					                            existingLocation.membersAvailable++;
 | 
				
			||||||
 | 
					                            if(availability.canHost) {
 | 
				
			||||||
 | 
					                                existingLocation.hostsAvailable++;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        else if(availability.canTravel) {
 | 
				
			||||||
 | 
					                            existingLocation.membersAvailable++;
 | 
				
			||||||
 | 
					                            existingLocation.travellersAvailable++;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if(locations.length > 0) {
 | 
				
			||||||
 | 
					                    header.appendChild(document.createElement("br"));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                locations.sort((a, b) => {
 | 
				
			||||||
 | 
					                    if(a.membersAvailable > b.membersAvailable) {
 | 
				
			||||||
 | 
					                        return -1;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    else if(a.membersAvailable < b.membersAvailable) {
 | 
				
			||||||
 | 
					                        return 1;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    return 0;
 | 
				
			||||||
 | 
					                }).forEach(location => {
 | 
				
			||||||
 | 
					                    let spanLocation = document.createElement("span");
 | 
				
			||||||
 | 
					                    spanLocation.textContent = location.location + location.membersAvailable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    let title = [location.location + ": "];
 | 
				
			||||||
 | 
					                    if(location.hostsAvailable > 0) {
 | 
				
			||||||
 | 
					                        title.push(location.hostsAvailable + "🏠");
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    if(location.travellersAvailable > 0) {
 | 
				
			||||||
 | 
					                        title.push(location.travellersAvailable + "🚅");
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    let neitherHostNorTraveller = location.membersAvailable - location.hostsAvailable - location.travellersAvailable;
 | 
				
			||||||
 | 
					                    if(neitherHostNorTraveller > 0) {
 | 
				
			||||||
 | 
					                        title.push(neitherHostNorTraveller);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    spanLocation.setAttribute("title", title.join(" "));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    header.appendChild(spanLocation);
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                tbody.appendChild(clonedRow);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        data2dom(viewData);
 | 
				
			||||||
 | 
					    </script>
 | 
				
			||||||
</body>
 | 
					</body>
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
		Loading…
	
		Reference in a new issue