Using CSS to design a table-like structure with rows that span multiple columns

I am trying to generate a table dynamically using CSS and a JSON array.

For example: In the code snippet provided, I have assigned a class of 'spannedrow' to span certain cells in the table, although the class is not defined yet. This is just for demonstration purposes. I require assistance in creating such a class.

var div="<div>";
    for(var i=0;i<myArr.length;i++) {     
        var arrHobies = myArr[i]['Hobbies'];
        var arrSkills = myArr[i]['Skills'];
        var arrLanguage = myArr[i]['Language'];
         div+="<div>";
         div+="<div class='spannedrow'>"+myArr[i]['Id']+"</td>";
         div+="<div class='spannedrow'>"+myArr[i]['Name']+"</td>";
         div+="<div class='spannedrow'>"+myArr[i]['Age']+"</td>";
         for(var j=0;j<arrHobies.length;j++ ) {
              div+="<div class='singlerow'>"+arrHobies[j]['HobbyId']+"</td>";
              div+="<div class='singlerow'>"+arrHobies[j]['HobbyName']+"</td>";
         }
          for(var l=0;l<arrSkills.length;l++ ) {
              div+="<div class='singlerow'>"+arrSkills[l]['SkillId']+"</td>";
              div+="<div class='singlerow'>"+arrSkills[l]['SkillName']+"</td>";
         }
         var div+="</div>"
    }
    var div+="</div>" ;  

Here is an example of my JSON array:

var myArr = [{
    "Id": 1,
    "Name": 'Ken',
    "Age": '30',
    "Hobbies": [{
      'HobbyId': 1,
      'HobbyName': 'Swimming'
    }, {
      'HobbyId': 2,
      'HobbyName': 'Reading'
    }],
    "Skills": [{
      'SkillId': 1,
      'SkillName': 'PHP'
    }, {
      'SkillId': 2,
      'SkillName': 'MySQL'
    }],
    "Language": [{
      'LangId': 2,
      'LangName': 'English'
    }, {
      'LangId': 3,
      'LangName': 'Chinese'
    }]
  },
  {
    "Id": 2,
    "Name": 'Mike',
    "Age": '20',
    "Hobbies": [],
    "Skills": [],
    "Language": []
  },
  {
    "Id": 3,
    "Name": 'Charlie',
    "Age": '25',
    "Hobbies": [{
      'HobbyId': 5,
      'HobbyName': 'Dance'
    }, {
      'HobbyId': 6,
      'HobbyName': 'Sing'
    }, {
      'HobbyId': 7,
      'HobbyName': 'Writing'
    }],
    "Skills": [],
    "Language": [{
      'Id': 7,
      'Name': 'English'
    }]
  }
]

The table representation of this array can be seen here.

Would it be possible to achieve row spanning with CSS styles?

I am now incorporating actual dynamic data into the mix.

 var myArr = [
            {
                "ItemMasterNumber":"290015","IM_Description":"XXX5","IM_FirstProcessDate":"10-19-2016",
                "IM_Alias":"test"                
                "ItemFeatureSet":
                        [{"FS_Id":"2002","FS_Code":"XXX5","FS_Name":"XXX5","FS_Description":"XXX5"}],
                "ItemFeatures":
                      [{"FE_Id":"1864","FE_Value":"VERSION","FE_Name":"2017"},
                       {"FE_Id":"1865","FE_Value":"EDITION","FE_Name":"Deluxe"}],
                "ItemCharges":[
                    {"CH_ChargeId":"23004746","CH_Name":"XXX5","CH_Description":"",
                        "CH_Type":"One Time"
                     }],
                "ItemChargeAttributes":[
                         {"CA_Id":"1628","CA_ListPrice":"99","CA_FairValueBasis":"BESP"}],
                "ItemPackages":[{"PA_PackageId":"21004482"}],
                "ItemPackagesComponents":[{"PA_Id":"9189","PA_Type":"Feature Set"},{"PA_Id":"9190","PA_Type":"Charge"}],
                "ItemOffers":[{"OF_OfferId":"20003880","OF_Name":"XXX5"}],
                "ItemOffersComponents":[{"OC_Id":"3877","OC_Quantity":"","OC_AdjustmentAmount":""}]
            }];

After attempting to use the above real array, some data mixing issues were encountered with the following code:

<html>
  <head>
    <title>TODO supply an appropriate title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script>
        $( document ).ready(function() {
        var myArr = [
            {
                "ItemMasterNumber":"290015","IM_Description":"XXX5","IM_FirstProcessDate":"10-19-2016",
                "IM_Alias":"test"                
                "ItemFeatureSet":
                        [{"FS_Id":"2002","FS_Code":"XXX5","FS_Name":"XXX5","FS_Description":"XXX5"}],
                "ItemFeatures":
                      [{"FE_Id":"1864","FE_Value":"VERSION","FE_Name":"2017"},
                       {"FE_Id":"1865","FE_Value":"EDITION","FE_Name":"Deluxe"}],
                "ItemCharges":[
                    {"CH_ChargeId":"23004746","CH_Name":"XXX5","CH_Description":"",
                        "CH_Type":"One Time"
                     }],
                "ItemChargeAttributes":[
                         {"CA_Id":"1628","CA_ListPrice":"99","CA_FairValueBasis":"BESP"}],
                "ItemPackages":[{"PA_PackageId":"21004482"}],
                "ItemPackagesComponents":[{"PA_Id":"9189","PA_Type":"Feature Set"},{"PA_Id":"9190","PA_Type":"Charge"}],
                "ItemOffers":[{"OF_OfferId":"20003880","OF_Name":"XXX5"}],
                "ItemOffersComponents":[{"OC_Id":"3877","OC_Quantity":"","OC_AdjustmentAmount":""}]
            }];
console.log(JSON.stringify(myArr));
// Assuming it's not going to be empty
var headers = ["Item Number", "Description", "First Process Date", "Alias", "Master Price", "Product Id", "Product Description",
    "FeatureSet #","Feature Set Code","Name","Description","Enablement Type",
    "Feature Id","Value","Name",
    "Charge Id","Name","Description","Type",
     "Record Id","List Price","Fair Value Basis","Fair Value Low","Fair Value High","Effective SD",
    "Package Id",
    "Record Id","Component Type",
    "Offer Id","Name","Description","Type","Level","Effective SD",
    "Record Id","Quantity","Adjustment Amt"

    ];

var containers = ["ItemFeatureSet", "ItemFeatures", "ItemCharges",
    "ItemChargeAttributes",
    "ItemPackages","ItemPackagesComponents","ItemOffers",
    "ItemOffersComponents"];

for (let header of headers)
{
    console.log('Ok');
    console.log('${header}')
  $("#headers").append(`<th>${header}</th>`);
}

// foreach item
for (let group of myArr)
{
  let span = 1;

  // Sets the length of row span
  for (let container of containers)
  {
    span = group[container].length > span ? group[container].length : span;
  }

  // for the first/main row of each item
  let temp_tr = $("<tr>");
  for (let item in group)
  {
    // Checking if key is array
    if (Array.isArray(group[item]))
    {
      let insert_value = "";
      //If it is greater than 0 use value
      if (group[item].length)
      {
        let temp_keys = Object.keys(group[item][0]);
        for (let tk of temp_keys)
        {
          insert_value += `<td>${group[item][0][tk]}</td>`;
        }
      }
      else
      {
        insert_value += "<td></td><td></td>";
      }

      $(temp_tr).append(insert_value);
    }
    else
    {
      $(temp_tr).append(`<td rowspan=${span}>${group[item]}</td>`);
    }

  }
  // Add to tbody
  $("#body").append(temp_tr); 


  // for each inner item
  for (let i = 1; i < span; i++)
  {
    let temp_tr = $("<tr>");

    for (let item of containers)
    {
      let insert_value = "";

      // Only add values if there are any
      if (i < group[item].length)
      {
        let temp_keys = Object.keys(group[item][i]);
        for (let tk of temp_keys)
        {
          insert_value += `<td>${group[item][i][tk]}</td>`;
        }
      }
      else
      {
        insert_value += "<td></td><td></td>";
      }
      $(temp_tr).append(insert_value);  
      $("#body").append(temp_tr);
    }

  }


}
 });
    </script>
  </head>
  <body>
      <table id="mytable" border=1>
  <thead>
    <tr>
      <th colspan=7></th>
      <th colspan=5>Feature Set Related Data</th>
      <th colspan=3>Features Related Data</th>
      <th colspan=4>Charges Related Data</th>
      <th colspan=6>Charge Attribute Related Data</th>
      <th colspan=1>Package Related Info</th>
      <th colspan=2>Package Component Related Data</th>
      <th colspan=6>Offer Related Data</th>
      <th colspan=3>Offers Component Related Data</th>
    </tr>
    <tr id="headers">
    </tr>
  </thead>
  <tbody id="body">
  </tbody>
</table>
  </body>
</html>

Answer №1

If you need any clarification, feel free to ask about my answer. It might be lacking some validation somewhere, but I believe it's a solid starting point. Experiment with the border CSS properties to achieve your desired look.

var myArr = [{
    "Id": 1,
    "Name": 'Ken',
    "Age": '30',
    "Hobbies": [{
      'HobbyId': 1,
      'HobbyName': 'Swimming'
    }, {
      'HobbyId': 2,
      'HobbyName': 'Reading'
    }],
    "Skills": [{
      'SkillId': 1,
      'SkillName': 'PHP'
    }, {
      'SkillId': 2,
      'SkillName': 'MySQL'
    }],
    "Language": [{
      'LangId': 2,
      'LangName': 'English'
    }, {
      'LangId': 3,
      'LangName': 'Chinese'
    }]
  },
  {
    "Id": 2,
    "Name": 'Mike',
    "Age": '20',
    "Hobbies": [],
    "Skills": [],
    "Language": []
  },
  {
    "Id": 3,
    "Name": 'Charlie',
    "Age": '25',
    "Hobbies": [{
      'HobbyId': 5,
      'HobbyName': 'Dance'
    }, {
      'HobbyId': 6,
      'HobbyName': 'Sing'
    }, {
      'HobbyId': 7,
      'HobbyName': 'Writing'
    }],
    "Skills": [],
    "Language": [{
      'Id': 7,
      'Name': 'English'
    }]
  }
];

// Assuming it's not going to be empty
var headers = ["Id", "Name", "Age", "HobbyId", "HobbyName", "SkillId", "SkillName", "Id", "Name"];

var containers = ["Hobbies", "Skills", "Language"];

for (let header of headers)
{
  $("#headers").append(`<th>${header}</th>`);
}

// foreach item
for (let group of myArr)
{
  let span = 1;
  
  // Sets the length of row span
  for (let container of containers)
  {
    span = group[container].length > span ? group[container].length : span;
  }
  
  // for the first/main row of each item
  let temp_tr = $("<tr>");
  for (let item in group)
  {
    // Checking if key is array
    if (Array.isArray(group[item]))
    {
      let insert_value = "";
      //If it is greater than 0 use value
      if (group[item].length)
      {
        let temp_keys = Object.keys(group[item][0]);
        for (let tk of temp_keys)
        {
          insert_value += `<td>${group[item][0][tk]}</td>`;
        }
      }
      else
      {
        insert_value += "<td></td><td></td>";
      }
      
      $(temp_tr).append(insert_value);
    }
    else
    {
      $(temp_tr).append(`<td rowspan=${span}>${group[item]}</td>`);
    }

  }
  // Add to tbody
  $("#body").append(temp_tr); 
  
  
  // for each inner item
  for (let i = 1; i < span; i++)
  {
    let temp_tr = $("<tr>");
    
    for (let item of containers)
    {
      let insert_value = "";

      // Only add values if there are any
      if (i < group[item].length)
      {
        let temp_keys = Object.keys(group[item][i]);
        for (let tk of temp_keys)
        {
          insert_value += `<td>${group[item][i][tk]}</td>`;
        }
      }
      else
      {
        insert_value += "<td></td><td></td>";
      }
      $(temp_tr).append(insert_value);  
      $("#body").append(temp_tr);
    }
    
  }

  
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="mytable" border=1>
  <thead>
    <tr>
      <th colspan=3></th>
      <th colspan=2>Hobbies</th>
      <th colspan=2>Skills</th>
      <th colspan=2>Language</th>
    </tr>
    <tr id="headers">
    </tr>
  </thead>
  <tbody id="body">
  </tbody>
</table>

Answer №2

If you want to create a table layout, consider this structure:

var dataArr = [{
    "ID": 1,
    "Name": 'Ken',
    "Age": '30',
    "Interests": [{
      'InterestID': 1,
      'InterestName': 'Swimming'
    }, {
      'InterestID': 2,
      'InterestName': 'Reading'
    }],
    "Skills": [{
      'SkillID': 1,
      'SkillName': 'PHP'
    }, {
      'SkillID': 2,
      'SkillName': 'MySQL'
    }],
    "Language": [{
      'LangID': 2,
      'LangName': 'English'
    }, {
      'LangID': 3,
      'LangName': 'Chinese'
    }]
  },
  {
    "ID": 2,
    "Name": 'Mike',
    "Age": '20',
    "Interests": [],
    "Skills": [],
    "Language": []
  },
  {
    "ID": 3,
    "Name": 'Charlie',
    "Age": '25',
    "Interests": [{
      'InterestID': 5,
      'InterestName': 'Dance'
    }, {
      'InterestID': 6,
      'InterestName': 'Sing'
    }, {
      'InterestID': 7,
      'InterestName': 'Writing'
    }],
    "Skills": [],
    "Language": [{
      'LangID': 7,
      'LangName': 'English'
    }]
  }
];

function generateRows(object, maxRow, id, name) {
  var tableContent = '';
  if (object.length || maxRow) {
      tableContent += "<table>";
      object.length && $(object).each(function() {
          tableContent += '<tr><td width="40%">' + this[id] + '</td><td width="60%">' + this[name] + '</td></tr>';
      });
      if (object.length<maxRow){
        for(var i=0,l=maxRow-object.length;i<l;i++){
          tableContent += '<tr><td width="40%">&nbsp;</td><td  width="60%">&nbsp;</td></tr>';
        }
      }
      tableContent += '</table>';
    }
    return tableContent;
}

var tableContent = "<table border='1' style='border:1px solid #000;' cellpadding='0' cellspacing='0' >";
tableContent += '<tr><td colspan="3"></td>' +
  '<td>Interests</td><td>Skills</td><td>Languages</td></tr>';
tableContent += '<tr><td>ID</td><td>Name</td><td>Age</td>' +
  '<td><table><tr><td width="40%">Interest ID</td><td  width="60%">Interest Name</td></tr></table>'+
  '<td><table><tr><td width="40%">Skill ID</td><td  width="60%">Skill Name</td></tr></table>'+
  '<td><table><tr><td width="40%">Language Id</td><td  width="60%">Language Name</td></tr></table>';
for (var i = 0; i < myArr.length; i++) {
  var interestsArray = myArr[i]['Interests'];
  var skillsArray = myArr[i]['Skills'];
  var languageArray = myArr[i]['Language'];
  var maximumRows=Math.max(interestsArray.length,skillsArray.length,languageArray.length);
  tableContent += "<tr>";
  tableContent += "<td valign='top' class='pad-5'>" + myArr[i]['ID'] + "</td>";
  tableContent += "<td valign='top' class='pad-5'>" + myArr[i]['Name'] + "</td>";
  tableContent += "<td valign='top' class='pad-5'>" + myArr[i]['Age'] + "</td>";
  tableContent += '<td>'+
          generateRows(interestsArray,maximumRows,'InterestID','InterestName')+
          '</td>';
  tableContent += '<td>'+
          generateRows(skillsArray,maximumRows,'SkillID','SkillName')+
          '</td>';
  tableContent += '<td>'+
          generateRows(languageArray,maximumRows,'LangID','LangName')+
          '</td>'; 
  tableContent += "</tr>"
}
tableContent += "</table>";
$(tableContent).appendTo('body');
table {
  border-collapse:collapse;
  width:100%;
}
table table td {
  border: 1px solid black;
  padding:5px;

}
.pad-5{padding:5px}
table table tr:first-child td {
  border-top: 0;
}
table table tr:last-child td {
  border-bottom: 0;
}
table table tr td:first-child {
  border-left: 0;
}
table table tr td:last-child{
  border-right: 0;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

This updated content reflects handling dynamic information:

var dataArr = [` Updated data array goes here `];
var newTable = '<table border="1"><tr>';
        tblData=[],
        isFirstRow = true;

        for (var i = 0, cols = Object.keys(dataArr[0]), l = cols.length; i < l; i++) {
            newTable += '<th>' + cols[i] + '</th>';
        };
        newTable += '</tr>';

        tblData.push(newTable);
        $.each(dataArr, function(index, obj) {
            newTable = '<tr>';

            $.each(obj, function(key, value) {
                isFirstRow = !index;
                if ($.isArray(value)) {
                    if (isFirstRow) {
                        newTable += '<td><table><tr>';

                        for (var i = 0, cols = Object.keys(value[0]), l = cols.length; i < l; i++) {
                            newTable += '<th>' + cols[i] + '</th>';
                        }
                        newTable += '</tr>';
                        isFirstRow=false;
                    } else {
                        newTable += '<td><table>';
                    }               
                    $.each(value, function(k, val) {
                        newTable += '<tr>';
                        $.each(val, function(j, v) {
                            newTable += '<td> ' + v + ' </td>';
                        });
                        newTable += '</tr>';
                    });
                    newTable += '</table></td>';
                } else {
                    value = value.trim().length==0 ? '&nbsp;':value;
                    newTable += '<td> ' + value + ' </td>';
                }


            });
            isFirstRow = false;
            newTable += '</tr>';
            tblData.push(newTable);
        });

        $(tblData.join()).appendTo('body');

        $('table th').each(function(){
            $(this).text(function(){
                return $(this).text().replace(/([A-Z])/g, ' $1').replace('_',' ')
                .replace(/^./, function(str){ return str.toUpperCase(); })
        });
    });
table {
  border-collapse: collapse;
  width: 100%;
}

table table td,
table table th {
  border: 1px solid black;
}

.pad-5 {
  padding: 5px
}

table table tr:first-child td,
table table tr:first-child th {
  border-top: 0;
}

table table tr:last-child td,
table table tr:last-child th {
  border-bottom: 0;
}

table table tr td:first-child,
table table tr th:first-child {
  border-left: 0;
}

table table tr td:last-child,
table table tr th:last-child {
  border-right: 0;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

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

The appearance of online and local websites varies on a single screen and browser

My current dilemma revolves around the difference in appearance of my website when viewed locally versus online. Both versions are connected to the same git repository and use the same css file. Interestingly, I can view the page on both my local machine a ...

Why is my JQuery UI droppable accept condition failing to work?

After scouring the internet for hours, I'm still stuck and can't seem to figure out what's wrong with my code: Here's the HTML snippet: <ul style="list-style:none;cursor:default;"> <li>uuu</li> <li>aaa& ...

How can I notify an error in CoffeeScript/JavaScript when a parameter is not provided?

Initially, I believed that my code was clever for accomplishing this task: someFunction = (arg1, arg2, arg3) -> if _.some(arguments, (a) -> a is undefined) throw new Error "undefined parameter" My goal was to throw an error if any of the para ...

Determining the selected option's value made by the user

On my index.php file, which contains the form and function, I have the following code: $(function() { $.ajax({ type: "POST", url: "invtype.php", data: "getrevenuetype=true", success: function(b){ $("#revenue ...

Is it possible for me to activate a function on a different component using my header?

Hi there, I'm curious about how Vue routing and the tree structure work together. In my setup, I have a parent router that contains both my router-view and header at the same level. I want to be able to trigger some functions from my header component ...

Breaking Down and Optimizing your Code with Destructuring and

Currently, I am performing some basic destructuring in Javascript: //@flow "use strict"; (function(){ const {a,c} = check(true); })(); function check(bool:boolean):{|a:string,c:string|}|{||}{ if(bool){ return { a:"b", c:"d" } ...

Do Material-UI pickers only validate on blur, not on change events?

When you type inside the DatePicker component, validation is triggered immediately. Is there a way to trigger validation on blur instead of onChange when using @material-ui/pickers with material-ui v4 Passing the value to the blur function should work a ...

Using the combination of z-index and visibility:hidden makes the button go completely undetect

Can someone please review this code? .parent { visibility: hidden; position: relative; z-index: 1; } .child { visibility: visible; } <button class="parent"> <span class="child">content</span> </button> I' ...

Merging arrays with the power of ES6 spread operator in Typescript

My goal is to merge two arrays into one using the spread object method as shown in the code snippet below: const queryVariable = { ...this.state, filters: [...Object.keys(extraFilters || {}), ...this.state.filters], } The this.state.filte ...

Unpredictable algorithm for selecting the champion

I manage a website that features promotions and users. Users have the ability to register for these promotions, with one random user selected as the winner. Initially, the formula for selecting the winner was quite simple: $total_users = 100; //total use ...

Is there a way to prevent the typing in a browser textbox from being affected by keyup events when sending a request?

On a website, there is a textbox that allows the user to input their text and receive results by changing the content through an AJAX request response. The issue arises when typing too quickly as the response may not keep up with the typing speed, resulti ...

Creating a popup trigger in ReactJS to activate when the browser tab is closed

I am currently working on an enrollment form that requires customer information. If a user fills out half of the form and then attempts to close the tab, I want to trigger a popup giving them the option to save and exit or simply exit. While I have a jQue ...

Transforming FullCalendar (jquery) into asp.net (ashx)

///////////// Expert //////////////// $(document).ready(function() { var date = new Date(); var d = date.getDate(); var m = date.getMonth(); var y = date.getFullYear(); $('#calendar').fullCalendar({ ...

What causes the transformation from "&" to "&amp;" upon the completion of page loading?

While using Firefox and working on a page, I noticed that & turns into &amp;. Normally, I would fix this with html_entity_decode(), but it's not working in this case. Then I stumbled upon something interesting. An alert is triggered once the ...

Having trouble making z-index work on a child div with absolute positioning

I am attempting to design a ribbon with a triangle div positioned below its parent div named Cart. <div class="rectangle"> <ul class="dropdown-menu font_Size_12" role="menu" aria-labelledby="menu1" style="min-width: 100px; z-index: 0"> & ...

Comparing JSON Objects in Javascript

I'm in the process of developing a web application that retrieves data from a server and displays it to the user. The script pulls data from the server every 10 seconds, and if there's any change in the data, it alerts the user. Currently, the co ...

The background image seems to be malfunctioning

I've been attempting to recreate a similar design to this website, and while most of it is working smoothly, I'm encountering an issue with using ::before to add a background image as desired. Below is the code snippet in question: /* Section ...

Decoding SQS POST messages using node.js

I am faced with the challenge of setting up communication between a web server and a worker using an SQS. The process involves uploading an image to an S3 bucket through the server, which then sends a message to the SQS for the worker to retrieve, resize, ...

Finding maximum values by key in Mongoose

Welcome to My Schema let schema = new mongoose.Schema({ property: String, numericValue: Number }, { timestamps: true }); In my database, I store various statistics like: let entryA = { property: "visitors", numericValue: 120 } let en ...

Exploring Bootstrap datatables to search through nested table data with Codeigniter

I have implemented a table using bootstrap datatables and successfully enabled search functionality within the table. However, I have also included nested tables within each row of the main table. These nested tables are supposed to be displayed when clic ...