Creating a visual comparison by using knockout side by side

I'm currently working on a project that requires me to display multiple items side by side for comparison.

The ideal layout would be similar to Amazon's, where each item is represented by a vertical column with all relevant information about that item:

https://i.stack.imgur.com/Tvje5.png

My current framework is Knockout, and I have an Observable array with the items I want to compare. I plan to use the foreach binding, but I am facing a challenge due to the nature of tables having all cells for a specific row contained within the same element:

<tr>
  <td>Rating</td>
  <td> 4.5 (data from item 1)</td>
  <td> 3.5 (data from item 2)</td>
</tr>
<tr>
  <td>Price</td>
  <td>$2.3 (data from item 1) </td>
  <td>$3.5 (data from item 2) </td>
</td>

If I were to contain each item in a separate row, this problem would not exist. Does anyone have any suggestions on how to address this issue, given that data for different items needs to be in different (overlapping) parts of the document?

Answer №1

Unfortunately, I have some discouraging news to share with you. Html tables follow a strict row-based order, which can make organizing your data in this manner quite challenging.

You are faced with two potential solutions:

Option A: Restructure Your Data

You could create a specialized View Model for a "Feature-Comparison" and reorganize your data accordingly. By transposing your data in this way, you can then utilize a foreach loop within your tbody to iterate through these "featureComparisons". Here is an example of how this can be implemented in code:

var inputData = [
  { name: "First product", rating: 4.5, price: "$3.50", color: "Gray" },
  { name: "Another product", rating: 3.5, price: "$2.95", color: "Yellow" }
];

function FeatComparison(comparisonName, comparedValues) {
  this.comparisonName = comparisonName;
  this.comparedValues = comparedValues;
}

var vm = { comparisons: ko.observableArray([]) };

// Alternatively, you can explicitly receive a list of comparable properties from the backend.
// In this example, we iterate over the first product.
for (var prop in inputData[0]) {
  if (inputData[0].hasOwnProperty(prop)) {
    var vals = inputData.map(function(i) { return i[prop]; });
    vm.comparisons.push(new FeatComparison(prop, vals));
  }
}

ko.applyBindings(vm);
td { background: #eee; padding: 5px 10px; }
tr:first-child, td:first-child { font-weight: bold; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<table><tbody data-bind="foreach: comparisons">
  <tr>
    <td data-bind="text: comparisonName"></td>
    <!-- ko foreach: comparedValues -->
    <td data-bind="text: $data"></td>
    <!-- /ko -->
  </tr>
</tbody></table>

This particular option works best for scenarios where you need to facilitate detailed comparisons, such as comparing multiple products or allowing users to customize which features are included in the comparison.

Option B: Embrace the Challenge (due to lack of a more fitting title)

If restructuring your data seems like too much effort, you can opt to leave it as is and simultaneously iterate through both objects using foreach. The following code demonstrates how this can be done:

var inputData = [
  { name: "First product", rating: 4.5, price: "$3.50", color: "Gray" },
  { name: "Another product", rating: 3.5, price: "$2.95", color: "Yellow" }
];

function RootVm(data) {
  var self = this;
  this.products = ko.observableArray(data);
  this.keys = ko.computed(function() {
    if (self.products().length === 0) return [];
    return Object.keys(self.products()[0]);
  });
}

ko.applyBindings(new RootVm(inputData));
td { background: #eee; padding: 5px 10px; }
tr:first-child, td:first-child { font-weight: bold; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<table><tbody data-bind="foreach: keys">
  <tr>
    <td data-bind="text: $data"></td>
    <!-- ko foreach: $root.products -->
    <td data-bind="text: $data[$parent]"></td>
    <!-- /ko -->
  </tr>
</tbody></table>

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

How can I adjust a number using a shifter in JavaScript?

Searching for an event handler where I can use a shifter to adjust the value of a number by moving it left or right. Would appreciate any links to documentation on how to achieve this. Many thanks UPDATE Thanks to the suggestions from other users, I hav ...

utilizing eval() in order to retrieve a pointer to an include

I am currently developing a form widget where the form schema is fetched from an API and generated dynamically. The library I am using for this purpose (vue-form-generator) comes with built-in validators that are directly referenced within the library itse ...

Solution for repairing the display location button on Android within a React Native Map

I am facing an issue with the Location Button in my React Native Maps app. When the app is opened for the first time and the map is rendered, the button disappears. However, if I close the app but leave it running in the background, upon reopening the app, ...

What are the necessary conditions for executing npm start?

I'm encountering issues running npm start in my project, which is linked to my package.json file below: { "name": "babek", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test speci ...

Having issues sending multiple variables to PHP through Ajax

Trying to pass three variables through the URL using Ajax - one constant and two from a date picker. The constant passes fine, but the date variables are only passing as their names. Here's the code for the date pickers: <tr> ...

What is the best method for converting IDs into objects within ng-options in Angular?

Is there a way to dynamically use an array of IDs as the source of my ng-option directive inside of select? Instead of creating an array of objects with corresponding IDs, I am wondering if there is a method to set a function as the source of ng-option. ...

Click to Rotate the Shape

I am attempting to apply a rotation effect on a shape when clicked using jQuery and CSS. My goal is to select the element with the id of x and toggle the class rotate on and off. The rotate class utilizes the CSS transform property to achieve the desired r ...

Ways to conceal images until AFTER the completion of the jquery flexslider loading process

After trying to integrate wootheme's Flexslider on my website, I encountered a small issue with its loading process. Whenever the page is refreshed with the slider, there is a brief moment (approximately 1 second) where the first slide appears overly ...

I'm having trouble getting EJS files to run JavaScript module scripts

I am facing an issue with my ejs file running on localhost. I am trying to execute functions from other files that are imported with js. In the ejs file, there is a module script tag which successfully executes the code of the file specified in the src att ...

Challenge with maintaining tab view data in Openui5

I am facing an issue with my application's tabs. Each tab renders data retrieved through ajax calls from the database. However, whenever I switch between tabs, the data gets refreshed each time. Is there a way to prevent this refreshing behavior and i ...

Mobile browser experiences video freezing when src is set through useState, yet it starts playing when triggered by a button click or when the video is set to M

Hey awesome folks at the StackOverflow Community, I'm currently encountering a perplexing issue with a video element in my web application. The video is supposed to play automatically on both desktop and mobile browsers. However, I've run into a ...

The evaluation of CKEDITOR's execution code

Every time I input my content into CKEDITOR, I receive the following error: "Unexpected token < " This is the code I am using: eval('CKEDITOR.instances.'+ckeditorID+'.insertHtml('+text+')'); The content of variable text ...

A step-by-step guide on accessing a JSON configuration file and configuring the parameter for AJAX requests

I have a configuration file named server.json which contains server details in JSON format. { "server": "127.0.0.1" } My objective is to retrieve the value of 'server' from this configuration file and use it in my jQuery functions. For exa ...

Toggle JavaScript Query Display

Welcome to my HTML test website where I am testing show/hide JavaScript functionality. Upon loading the page, you may notice that everything is hidden until a button is clicked. <html> <head><title>Test</title> <scr ...

A straightforward redirection in Express involving a static file

Just starting out with Node and Express and encountering a bit of trouble. I have a static html page where users enter their username via ajax to my server, and then I want to redirect them to another html file. const express = require("express"); const b ...

Modifying the src attribute of an object tag on click: A step-by

So I have an embedded video that I want to dynamically change when clicked on. However, my attempt at doing this using JavaScript doesn't seem to be working. <object id ="video" data="immagini/trailer.png" onclick="trailer()"></object> H ...

Enhance Website User Experience: Keeping Track of Scroll Position When Users Navigate Back in Browser

Have you ever experienced the frustration of scrolling through a page with infinite scroll, only to click on a link and then go back to find that you've lost your place in the scroll? You're scrolling down a lot, You click on a link from the i ...

Transform the JavaScript function to a Node.js module

My function serves as an abstract factory for creating JavaScript objects. Here is the code: var $class = function(definition) { var constructor = definition.constructor; var parent = definition.Extends; if (parent) { var F = function ...

Having an issue with my application crashing and showing the message "[nodemon] app crashed - waiting for file changes before starting...". Does anyone know how to

mainapp.js const PORT = 3000 const express = require('express') const axios = require('axios') const cheerio = require('cheerio') const app = express() app.listen(PORT, ()=>console.log('Server running on port ${PORT} ...

Using Ajax in JSF to dynamically render Bootstrap dropdown

I have created a form with dropdown menus, where users need to make their first selection and then additional choices appear using ajax. Everything is working fine. However, I decided to add bootstrap formatting to the dropdowns by installing bootstrap-sel ...