0

I would like to inject a independent div element into a table styled: display: table.

The div automatically takes the properties of a table-cell element, especially the width; How do I get it to be the width of 100%?

enter image description here

Here my codepen to test: codepen.io

.wrapper {
  margin: 0 auto;
  padding: 40px;
  max-width: 800px;
}

.herotable {
  background-color: #fff;
  padding: 1.5em;
  box-shadow: 3px 3px 20px rgba(0, 0, 0, .3);
  border-radius: 3px;
}

.table {
  margin: 0 0 10px 0;
  width: 100%;
  display: table;
}

.table.list {
  display: contents;
}

.row {
  display: table-row;
  background: var(--colorEven);
  /*   #f6f6f6;    */
}

.row:nth-of-type(odd) {
  background: var(--colorOdd);
  /*  #e9e9e9;  */
}

.row.header {
  background: #fff;
}

.cell {
  padding: 6px 12px;
  display: table-cell;
  position: relative;
}

.row .cell {
  border-top: 1px solid #ddd;
}

.row .cell:last-child {
  border-right-width: 0
}

.list .row:last-child .cell {
  border-bottom: 1px solid;
}

.row.header .cell {
  cursor: pointer;
  border: none;
  border-bottom: 1px solid;
}

.orders {
  display: inline-block;
  width: 100%;
  margin: 10px 0 10px 0;
  border: solid 1px #ccc;
  border-radius: 5px;
  padding: 10px;
}

.order {}
<div class="wrapper herotable">


  <div id="test">

    <div class="table">
      <div class="row header">
        <div class="cell sorting sort" data-sort="name">Name</div>
        <div class="cell sorting sort" data-sort="age">Age</div>
        <div class="cell sorting sort" data-sort="ocupat">Occupation</div>
        <div class="cell sorting sort" data-sort="location">Location</div>
      </div>

      <div class="table list">

        <div class="row">
          <div class="cell name" data-title="Name">Luke Peters</div>
          <div class="cell age" data-title="Age">25</div>
          <div class="cell ocupat" data-title="Occupation">Freelance Web Developer</div>
          <div class="cell location" data-title="Location">Brookline, MA</div>
        </div>


        <div class="orders">
          <div class="order">This should be an independent div-element. I would like to inject it by JavaScript when clicking on the line above.<br> It should not expand the name column.</div>
        </div>

        <div class="row">
          <div class="cell name" data-title="Name">Joseph Smith</div>
          <div class="cell age" data-title="Age">27</div>
          <div class="cell ocupat" data-title="Occupation">Project Manager</div>
          <div class="cell location" data-title="Location">Somerville, MA</div>
        </div>

        <div class="row">
          <div class="cell name" data-title="Name">Maxwell Johnson</div>
          <div class="cell age" data-title="Age">26</div>
          <div class="cell ocupat" data-title="Occupation">UX Architect & Designer</div>
          <div class="cell location" data-title="Location">Arlington, MA</div>
        </div>

      </div>
    </div>

  </div>

</div>

I do not ask for a JavaScript solution, I do only have a problem with CSS. But JavaScript solutions are welcome.

David Thomas
  • 249,100
  • 51
  • 377
  • 410
hamburger
  • 1,339
  • 4
  • 20
  • 41
  • You say the element should be inserted by clicking on the row above, how are you handling that interaction, with JavaScript or CSS? Is that part of the problem you're facing, or do you have that interaction already established, if you have it already can you share it with us? – David Thomas May 25 '21 at 12:56
  • Could you use css' grid layout instead of table? That should simplify it a bit – Ant May 25 '21 at 14:39

2 Answers2

1

The easiest means of achieving the layout requirements is to use CSS grid, along with grid-template-columns: subgrid, unfortunately this is (currently) supported only in FireFox version 71+, which represents almost 3%1 of global browser use. As such, while it works, it isn't really viable for a production site in the wild.

That said, the relevant HTML and CSS is below:

// here we cache a reference to all .row elements which are not .header elements,
// using document.querySelectorAll(), which returns a non-live NodeList:
const rows = document.querySelectorAll('.row:not(.header)'),
  // declaring a function, using Arrow syntax (since we don't use 'this'
  // within the function body:
  isVisible = (el) => {
    // if el is truthy, so is not 'null' or 'undefined':
    if (el) {
      // we return the results of the following assessments:
      // el.hidden === false: we test whether the hidden property
      //                      evaluates to false; if so:
      // window.getComputedStyle(el, null).display !== 'none':
      //                      recovers the rendered 'display' property
      //                      of the element, and the assessment checks
      //                      that it is not equal to 'none';
      // if both assessments evaluate to true, we return true otherwise
      // we return false:
      return el.hidden === false && window.getComputedStyle(el, null).display !== 'none';
    }
    // by default, in the event of a non-existent element or null-reference,
    // is passed to the function we simply return false:
    return false;
  },
  // another function declared using Arrow syntax:
  orderToggle = (e) => {
    // 'e' is a reference to the Event Object passed from the
    // EventTarget.addEventListener() call (later);
    // activated is a reference to the element to which the
    // event-handler was bound:
    const activated = e.currentTarget,
      // potentialOrders is a reference to the nextElementSibling
      // of the current-target; the variable name refers to the
      // fact that there may not be a nextSiblingElement or
      // that the nextSiblingElement may not be the appropriate
      // element:
      potentialOrders = activated.nextElementSibling,
      // here we call the isVisible() function, above, to retrieve
      // a Boolean (true/false) to represent the current-visibility
      // of the potentialOrder element:
      currentVisibility = isVisible(potentialOrders);

    // if the potentialOrders element is truthy (exists, and does not
    // evaluate to either undefined or null), we test whether that
    // element matches the CSS selector passed to the Element.matches()
    // method:
    if (potentialOrders && potentialOrders.matches('.orders')) {
      // here we update the hidden property of the potentialOrders
      // element, isVisible() function returns true, we wish to hide
      // the element, so we set 'hidden' to true, otherwise we set
      // it to false (as returned by the isVisible() function):
      potentialOrders.hidden = currentVisibility;
    }
  };

// here we iterate over the NodeList of .row:not(.header) elements
// using NodeList.prototype.forEach() along with an Arrow function:
rows.forEach(
  // 'el' is a reference to the current Node of the NodeList over
  // which we're iterating:
  (el) => {
    // here we use EventTarget.addEventListener() to bind the anonymous
    // function of the addEventListener() method to:
    el.addEventListener('click', function(e) {
      // call the orderToggle() function, passing the Event
      // Object to the function:
      orderToggle(e)
    });
  });
/* setting common default properties for all elements,
   along with the ::before and ::after pseudo-elements: */
*,
::before,
::after {
  box-sizing: border-box;
  font-size: 1rem;
  line-height: 1.5;
  margin: 0;
  padding: 0;
}

.herotable {
  margin: auto 1em;
  width: 90vw;
}

/* using CSS grid layout: */
.table {
  display: grid;
  /* setting four columns of equal width:  */
  grid-template-columns: repeat(4, 1fr);
}

/* distinguishing the .header element to
   make the headings visually distinguishable,
   but do consider adding appropriate ARIA
   roles/attributes to the HTML for
   accessibility purposes: */
.header {
  font-weight: bold;
  border-bottom: 2px solid currentColor;
}

.table.list {
  /* placing this element full width across the
     defined grid-columns 1 is the first
     column, -1 represents the last column; this
     is shorthand for "grid-column-start: 1"
     and "grid-column-end: -1": */
  grid-column: 1 / -1;
  /* setting grid-row-gap of 0.5em, and a
     grid-column-gap of 0.25em; to separate
     cells (adjust to taste): */
  gap: 0.5em 0.25em;
}

.row {
  /* using CSS grid-layout: */
  display: grid;
  /* again positioning the elements
     to start in the first column
     and end in the last column: */
  grid-column: 1 / -1;
  /* here we use subgrid to have the
     grid of the .row elements match
     the columns defined on the parent
     element: */
  grid-template-columns: subgrid;
}

.row:not(.header) {
  /* used to indicate interactivity, this
     may or not be desired in your use-case,
     adjust to taste: */
  cursor: pointer;
}

.orders {
  /* we don't need to use subgrid here since
     the .orders element(s) are children of a
     .table element: */
  display: grid;
  grid-column: 1 / -1;
}

/* hiding elements with the hidden attribute,
   because we've overriden the default
   display of the .orders elements: */
.orders[hidden] {
  display: none;
}

.order {
  border: 1px solid currentColor;
  border-left-color: transparent;
  border-right-color: transparent;
}
<div class="wrapper herotable">

  <div id="test">

    <div class="table">
      <div class="row header">
        <div class="cell sorting sort" data-sort="name">Name</div>
        <div class="cell sorting sort" data-sort="age">Age</div>
        <div class="cell sorting sort" data-sort="ocupat">Occupation</div>
        <div class="cell sorting sort" data-sort="location">Location</div>
      </div>

      <div class="table list">

        <div class="row">
          <div class="cell name" data-title="Name">Luke Peters</div>
          <div class="cell age" data-title="Age">25</div>
          <div class="cell ocupat" data-title="Occupation">Freelance Web Developer</div>
          <div class="cell location" data-title="Location">Brookline, MA</div>
        </div>
        <!-- note that I've set the 'hidden' attribute on the element
             so that it is hidden on page-load; this may or may not
             be required for your use-case: -->
        <div class="orders" hidden>
          <div class="order">This should be an independent div-element. I would like to inject it with JavaScript when clicking on line above.<br> It should not expand the name column.</div>
        </div>

        <div class="row">
          <div class="cell name" data-title="Name">Joseph Smith</div>
          <div class="cell age" data-title="Age">27</div>
          <div class="cell ocupat" data-title="Occupation">Project Manager</div>
          <div class="cell location" data-title="Location">Somerville, MA</div>
        </div>

        <div class="row">
          <div class="cell name" data-title="Name">Maxwell Johnson</div>
          <div class="cell age" data-title="Age">26</div>
          <div class="cell ocupat" data-title="Occupation">UX Architect &amp; Designer</div>
          <div class="cell location" data-title="Location">Arlington, MA</div>
        </div>

      </div>
    </div>

  </div>

</div>

A version that works in both browsers that don't implement subgrid is below, this is much the same except that column-widths are set using CSS variables and can therefore be accessed, and updated if required, by JavaScript; that possibility is not, however, included in the demo:

const rows = document.querySelectorAll('.row:not(.header)'),
  isVisible = (el) => {
    if (el) {
      return el.hidden === false && window.getComputedStyle(el, null).display !== 'none';
    }
    return false;
  },
  orderToggle = (e) => {
    const activated = e.currentTarget,
      potentialOrders = activated.nextElementSibling,
      currentVisibility = isVisible(potentialOrders);

    if (potentialOrders && potentialOrders.matches('.orders')) {
      potentialOrders.hidden = currentVisibility;
    }
  };

rows.forEach(
  (el) => {
    el.addEventListener('click', function(e) {
      orderToggle(e)
    });
  });
:root {
  /* setting column-widths for each individual column: */
  --column1Width: 25%;
  --column2Width: 25%;
  --column3Width: 25%;
  --column4Width: 25%;
  /* I chose to use variables here, too, because some
     elements will have to be spaced using margins and
     others using gap; this allows consistency in
     sizing: */
  --horizontalGap: 0.25rem;
  --verticalGap: 0.5rem;
  /* and this is to ensure that all elements representing
     table-rows match the same width: */
  --fullWidth: 90vw;
}

/* simple CSS reset as above: */
*,
::before,
::after {
  box-sizing: border-box;
  font-size: 1rem;
  line-height: 1.5;
  margin: 0;
  padding: 0;
}

/* centering the 'table' on the page,
   and assigning its width: */
.herotable {
  margin: auto 1em;
  width: var(--fullWidth);
}

/* styling the table-header information for
   visual distinction: */
.header {
  font-weight: bold;
  border-bottom: 2px solid currentColor;
}

/* because this is a block-element, and not within
   a flex, or grid, element we use margin - along
   with the defined custom properties - to maintain
   consistent spacing: */
.table.list {
  margin-top: var(--verticalGap);
  margin-bottom: var(--verticalGap);
}

.row {
  /* using CSS flex layout: */
  display: flex;
  /* justifying the contents of the element
     with space-between, to separate them
     appropriately based on their size and
     the defined gaps: */
  justify-content: space-between;
  /* assigning the margin between vertically-
     adjacent elements: */
  margin-top: var(--verticalGap);
  margin-bottom: var(--verticalGap);
  /* assigning the gaps between adjacent-elements
     within the .row elements: */
  gap: var(--verticalGap) var(--horizontalGap);
}

.row:not(.header) {
  cursor: pointer;
}

/* assigning the flex-basis of each element
   .cell element based on its 'column' placement;
   this allows any column to be resized via
   JavaScript: */
.row .cell:nth-child(1) {
  flex: 1 0 var(--column1Width);
}

.row .cell:nth-child(2) {
  flex: 1 0 var(--column2Width);
}

.row .cell:nth-child(3) {
  flex: 1 0 var(--column3Width);
}

.row .cell:nth-child(4) {
  flex: 1 0 var(--column4Width);
}

.order {
  border: 1px solid currentColor;
  border-left-color: transparent;
  border-right-color: transparent;
}
<div class="wrapper herotable">

  <div id="test">

    <div class="table">
      <div class="row header">
        <div class="cell sorting sort" data-sort="name">Name</div>
        <div class="cell sorting sort" data-sort="age">Age</div>
        <div class="cell sorting sort" data-sort="ocupat">Occupation</div>
        <div class="cell sorting sort" data-sort="location">Location</div>
      </div>

      <div class="table list">

        <div class="row">
          <div class="cell name" data-title="Name">Luke Peters</div>
          <div class="cell age" data-title="Age">25</div>
          <div class="cell ocupat" data-title="Occupation">Freelance Web Developer</div>
          <div class="cell location" data-title="Location">Brookline, MA</div>
        </div>

        <div class="orders">
          <div class="order">This should be an independent div-element. I would like to inject it with JavaScript when clicking on line above.<br> It should not expand the name column.</div>
        </div>

        <div class="row">
          <div class="cell name" data-title="Name">Joseph Smith</div>
          <div class="cell age" data-title="Age">27</div>
          <div class="cell ocupat" data-title="Occupation">Project Manager</div>
          <div class="cell location" data-title="Location">Somerville, MA</div>
        </div>

        <div class="orders" hidden>
          <div class="order">This should be an independent div-element. I would like to inject it with JavaScript when clicking on line above.<br> It should not expand the name column.</div>
        </div>

        <div class="row">
          <div class="cell name" data-title="Name">Maxwell Johnson</div>
          <div class="cell age" data-title="Age">26</div>
          <div class="cell ocupat" data-title="Occupation">UX Architect &amp; Designer</div>
          <div class="cell location" data-title="Location">Arlington, MA</div>
        </div>

        <div class="orders" hidden>
          <div class="order">This should be an independent div-element. I would like to inject it with JavaScript when clicking on line above.<br> It should not expand the name column.</div>
        </div>

      </div>
    </div>

  </div>

</div>

JS Fiddle demo.

References:

Footnotes:

  1. See: https://caniuse.com/css-subgrid.
David Thomas
  • 249,100
  • 51
  • 377
  • 410
0

You need to do it with <table>, <tr> and <td> elements and you can use colspan attribute for a full width cell.

  • sorry there is no possibilty to use a "real" table like . I have to use div's with classes.
    – hamburger May 25 '21 at 12:54
  • @hamburger i don't know why there is no possibility to use real tags for you but if it helps i recommend to check [this answer](https://stackoverflow.com/a/2617923/10419068)
    – kamilgunduz May 25 '21 at 19:01