Question: Is it possible to draw a CSS box around multiple div elements with unique classes, and with no ability to add parent div tags into the HTML?
I'm trying to group multiple div classes together as part of a larger form. The HTML in the form itself cannot be modified to add additional parent div layers. The CSS and JavaScript are editable
Example: We have a large request form that has 50 questions, with 10 questions in each section of the form. We can draw boxes around those individual questions, but also want to draw a box around a section of questions.
Each div has its own unique class. This is something we cannot change, as it's supplied by the tool we use.
Here's an example of our HTML and CSS:
.form-field {
background-color: #eee;
padding: 15px;
/* border: 1px solid black; */
border-radius: 5px;
box-shadow: 4px 4px grey;
}
.form-field:valid {
background-color: #000;
padding: 15px;
/* border: 1px solid black; */
border-radius: 5px;
box-shadow: 4px 4px grey;
}
.form-field.select.optional.request_ticket_form_id {
background-color: #d9ead3;
padding: 15px;
/* border: 1px solid black; */
border-radius: 5px;
box-shadow: 4px 4px grey;
}
.form-field:hover {
background-color: #ccc;
}
<div class="form-field text optional request_custom_fields_12345">
<label id="request_custom_fields_12345_label" for="request_custom_fields_12345">Vest Size<span class="optional">(optional)</span></label>
<textarea name="request[custom_fields][12345]" id="request_custom_fields_12345" aria-required="false" aria-labelledby="request_custom_fields_12345_label"></textarea>
</div>
<br>
<div class="form-field text optional request_custom_fields_67890">
<label id="request_custom_fields_67890_label" for="request_custom_fields_67890">Vest Shape<span class="optional">(optional)</span></label>
<textarea name="request[custom_fields][67890]" id="request_custom_fields_67890" aria-required="false" aria-labelledby="request_custom_fields_67890_label"></textarea>
</div>
<br>
<div class="form-field text optional request_custom_fields_98765">
<label id="request_custom_fields_98765_label" for="request_custom_fields_98765">Vest Color<span class="optional">(optional)</span></label>
<textarea name="request[custom_fields][98765]" id="request_custom_fields_98765" aria-required="false" aria-labelledby="request_custom_fields_98765_label"></textarea>
</div>
<br>
<div class="form-field text optional request_custom_fields_23456">
<label id="request_custom_fields_98765_label" for="request_custom_fields_23456">Vest Color<span class="optional">(optional)</span></label>
<textarea name="request[custom_fields][23456]" id="request_custom_fields_23456" aria-required="false" aria-labelledby="request_custom_fields_23456_label"></textarea>
</div>
The part that does not seem to work for us is trying to say "draw box around 67890 to 23456" (and anything in between).
Yes. There is a possibility
if the sole purpose is to highlight, or add a "visual shape" on-screen without altering the DOM.
Create a <div> element which has CSS position fixed.
Add pointer-events: none; to that DIV in order to use it exclusively as a "drawing layer" on-top of your app but allow pointer events to propagate trough it.
Get all your elements on the page From-element and to-element. Get their coordinates using getBoundingClientRect. BCR is relative to the viewport, which is perfect to use on a position fixed element overlay.
Once you have the "min-max" coordinates (calculate to min top-left value and the max bottom-right value) having those two points you can now freely draw a rectangle on your canvas. Coincidentally it will be drawn exactly around all the desired elements.
Make sure to "redraw/reposition" the DIV on page resize, scroll, load, DOM ready - or on some button click - if needed.
Create a JavaScript function to be used like:
highlightBox(Elements_to_consider, start_index, end_index)
// DOM utility functions:
const ELS = (sel, par) => (par || document).querySelectorAll(sel);
const EL = (sel, par) => (par || document).querySelector(sel);
// Task:
const EL_highlightBox = EL("#highlightBox");
const highlightBox = (ELS_items, fr, to) => {
const BCR_fr = ELS_items[fr].getBoundingClientRect();
const BCR_to = ELS_items[to].getBoundingClientRect();
EL_highlightBox.style.cssText = `
top: ${BCR_fr.top}px;
left: ${BCR_fr.left}px;
height: ${BCR_to.top - BCR_fr.top + BCR_to.height}px;
width: ${BCR_to.left - BCR_fr.left + BCR_to.width}px;
`;
};
const drawHighlightBox = () => highlightBox(ELS(".item"), 2, 4);
addEventListener("resize", drawHighlightBox);
addEventListener("scroll", drawHighlightBox);
drawHighlightBox();
#highlightBox {
/* Important styles: */
position: fixed;
z-index: 1000;
pointer-events: none;
/* Other styles: */
background: hsla(100, 60%, 50%, 0.2);
outline: 4px solid hsla(100, 60%, 50%, 1);
}
.item {
margin: 10px 0;
padding: 60px;
border: 1px solid gray;
}
<div id="highlightBox"></div>
<div class="item">Item 0 (scroll down)</div>
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
<div class="item">Item 4</div>
<div class="item">Item 5</div>
<div class="item">Item 6</div>
<div class="item">Item 7</div>
<div class="item">Item 8</div>
<div class="item">Item 9</div>
<div class="item">Item 10</div>
Related
When the .post-item <div> is hovered I want to execute some specific styles (change background-color and cursor) but I don't want this to happen if the .rating-wrapper <div> is hovered too. This happens because I want the .rating-wrapper to do something different than the hover of its parent. Basic question: How to do only child's hover, ignoring the parent's hover
HTML:
<div class="post-item">
<div class="rating-wrapper">
<div class="upvote">
<img src="/images/upvote_arrow.png" alt="upvote" />
</div>
<div class="rating"></div>
<div class="downvote">
<img src="/images/downvote_arrow.png" alt="downvote" />
</div>
</div>
<span class="owner-data">
<img src="" alt="" class="owner-avatar" />
<span class="owner-username"></span>
</span>
<span class="creation-date"></span>
<div class="title"></div>
</div>
Since you want to change the style of the parent element based on a pseudo-class of the child element, this isn't really possible with CSS alone today.
You can do it with the :has() pseudo-class but that is currently only supported in Safari (with support for Chrome a few months away and no sign of it in Firefox, Edge, Opera or elsewhere).
#parent {
background: white;
border: solid black 1px;
padding: 2em;
max-width: 50%;
margin: auto;
}
#parent:hover:not(:has(#child:hover)) {
background: orange;
}
#child {
background: #aaa;
border: solid black 1px;
padding: 2em;
}
#child:hover {
background: green;
}
<div id="parent">
<div id="child"></div>
</div>
For a more reliable approach, you should probably look at adding a splash of JavaScript to the mix.
Use mouseenter and mouseleave events to modify the classes of the parent element, then reference the class in your stylesheet.
const parent = document.querySelector('#parent');
const child = document.querySelector('#child');
const enter = event => parent.classList.add('child-hover');
const leave = event => parent.classList.remove('child-hover');
child.addEventListener('mouseenter', enter);
child.addEventListener('mouseleave', leave);
#parent {
background: white;
border: solid black 1px;
padding: 2em;
max-width: 50%;
margin: auto;
}
#parent:hover:not(.child-hover) {
background: orange;
}
#child {
background: #aaa;
border: solid black 1px;
padding: 2em;
}
#child:hover {
background: green;
}
<div id="parent">
<div id="child"></div>
</div>
You can use this CSS Selector,
.post-item>:not(.rating-wrapper):hover {
background-color: white;
}
This will select all immediate children of .post-item which are not .rating-wrapper.
To change the block of the remaining items background color, you can enclose them in another div.
There is a css property called not property.The syntax is like:
:not(element) {
// CSS Property}
If you want to learn more, please visit this link:
https://www.geeksforgeeks.org/how-to-exclude-particular-class-name-from-css-selector/
The pointer-events CSS property sets under what circumstances (if any) a particular graphic element can become the target of pointer events.
try:
pointer-events: none
you can read more here: https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events
I have an application with "stacked" input fields like so:
As you can see from the image, when a field is receiving focus, I only want the borders of that particular input element to be highlighted. I also want to avoid a particularly thick border by having both a top and bottom border that corresponds to the same middle lines.
Here is a Fiddle: https://jsfiddle.net/3nL426uy/
Creating the stacked input fields and applying borders is not a problem. I simply remove the bottom border on all the input fields and on the last field, I apply a bottom border.
The issue comes in when I want to apply the focus styles. As it stands currently, simply applying a different border color (used red in the example to clearly show the contrast) results in a red bottom border and a gray top border of the input field below it. Now, I may be able to use an Id selector on that particular field and apply a full border, however, how do I remove the top border of the input field below it?
const inputWrappers = document.querySelectorAll(".input-wrapper")
const wrappers = [...inputWrappers]
wrappers.forEach(wrapper => {
wrapper.addEventListener('focusin', (event) => {
event.target.classList.add('selected');
});
wrapper.addEventListener('focusout', (event) => {
event.target.classList.remove('selected');
});
})
.wrapper {
display: flex;
flex-direction: column;
}
.item {
width: 300px;
height: 30px;
padding: 5px 15px;
border: 1px solid gray;
border-radius: 0;
border-bottom: 0;
}
/* Remove the default input focus-visible outline */
.item:focus-visible {
outline: 0;
}
.input-wrapper:last-child .item {
border-bottom: 1px solid gray;
}
.selected {
border: 1px solid red;
}
<div class="container">
<div class="input-wrapper" >
<input class="item" placeholder="Enter item..." />
</div>
<div class="input-wrapper">
<input class="item" placeholder="Enter item..." />
</div>
<div class="input-wrapper">
<input class="item" placeholder="Enter item..." />
</div>
<div class="input-wrapper">
<input class="item" placeholder="Enter item..." />
</div>
</div>
I'm not sure why you've used any JavaScript here? You can just set a new focus style in the :focus-visible CSS rule, you don't need to add and remove a class when the inputs receive and lose keyboard focus.
To collapse the margins, I'd recommend applying margin-top: -1px; to each of the siblings with borders. Then, to make sure the current one has its borders on top, you can give it a z-index above its siblings. I've also added a couple of lines here to ensure that z-index won't escape the .wrapper element by creating its own stacking context.
.wrapper {
display: flex;
flex-direction: column;
/* Create a new stacking context to contain z-index */
isolation: isolate;
transform: scale(1);
}
.item {
width: 300px;
height: 30px;
padding: 5px 15px;
border: 1px solid gray;
border-radius: 0;
}
/* Remove the default input focus-visible outline */
.item:focus-visible {
outline: 0;
border-color: red;
/* Setting a z-index on the focused element ensures its borders are on top */
position: relative;
z-index: 1;
}
.input-wrapper {
/* A negative top margin lets the borders collapse */
margin-top: -1px;
}
.container {
/* Offset the negative top margin of the items */
margin-top: 1px;
}
<div class="container">
<div class="input-wrapper">
<input class="item" placeholder="Enter item..." />
</div>
<div class="input-wrapper">
<input class="item" placeholder="Enter item..." />
</div>
<div class="input-wrapper">
<input class="item" placeholder="Enter item..." />
</div>
<div class="input-wrapper">
<input class="item" placeholder="Enter item..." />
</div>
</div>
you can add 'selected' class to the div element;
wrappers.forEach(wrapper => {
wrapper.addEventListener('focusin', (event) => {
event.target.parentNode.classList.add('selected');
});
wrapper.addEventListener('focusout', (event) => {
event.target.parentNode.classList.remove('selected');
});
})
and then use sibling selector x + y to select below div, remove the top boarder.
.selected .item{
border: 1px solid red;
}
/* remove below div top border */
.selected+.input-wrapper .item{
border-top: 0;
}
.input-wrapper:last-child.selected .item{
border-bottom: 1px solid red;
}
here is the whole example
https://jsfiddle.net/2tuzi/koqrcxaw/
<div class="abc"></div>
I need to delete this div if there is nothing in between the div tags means when div is empty.
I am approaching in CSS like this
div.abc:empty{display:none;}
But I have one problem if I use this method. If there is a single space between div, like <div> </div> :empty doesn't work.
div.abc { border: 1px solid red; height:50px; }
div.abc:empty{display:none;}
<div class="abc"></div>
<hr/>
<div class="abc"> </div>
As of November 2021 impossible without JavaScript. There is no trim in CSS (yet except in FireFox
Note this example will also hide divs that have pseudo class content
document.querySelectorAll(".abc")
.forEach(div => div.hidden = div.textContent.trim() === "")
// alternative if you want to use a class:
// div.classList.toggle("hide",div.textContent.trim() === "")
div.abc { border: 1px solid red; height:50px; }
div.pscontent:after { content: "Also will be hidden"}
div.abc:empty{display:none;}
<div class="abc"></div>
<hr/>
<div class="abc"> </div>
<hr/>
<div class="abc pscontent"></div>
To handle pseudo class content we can do this:
const hideEmpty = (sel, testPseudoContent) => {
const elems = document.querySelectorAll(sel)
elems.forEach((elem,i) => {
const text = [elem.textContent.trim()]
if (testPseudoContent) {
["before", "after"].forEach(ps => text.push(window.getComputedStyle(elem, ps).getPropertyValue("content").trim()))
}
elem.hidden = text.join('').length === 0;
})
};
hideEmpty('.abc')
hideEmpty('.def.pscontent', true)
div.abc {
border: 1px solid red;
height: 50px;
}
div.def {
border: 1px solid green;
height: 50px;
}
div.pscontent:after {
content: "Don't hide this"
}
<div id="div1" class="abc"></div>
<hr/>
<div id="div2" class="abc"> </div>
<hr/>
<div id="div5" class="def pscontent"></div>
Your code does work but in the near future because the Specification has changed to make :empty consider white spaces
Note: In Level 2 and Level 3 of Selectors, :empty did not match elements that contained only white space. This was changed so that that—given white space is largely collapsible in HTML and is therefore used for source code formatting, and especially because elements with omitted end tags are likely to absorb such white space into their DOM text contents—elements which authors perceive of as empty can be selected by this selector, as they expect. ref
Until then, there is a Firefox solution using -moz-only-whitespace
div.abc { border: 1px solid red; height:50px; }
div.abc:-moz-only-whitespace {display:none;}
div.abc:empty {display:none;}
<div class="abc"></div>
<hr/>
<div class="abc"> </div>
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%?
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.
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 & 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 & 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:
CSS:
Attribute selectors ([attribute]).
CSS custom properties.
:not().
display.
gap.
margin.
padding.
repeat().
:root.
var() function.
HTML:
hidden attribute.
JavaScript
Arrow functions.
document.querySelector().
document.querySelectorAll().
Element.nextElementSibling.
HTMLElement.hidden.
Logical AND (&&).
NodeList.prototype.forEach().
Node.currentTarget.
Node.target.
Window.getComputedStyle().
Footnotes:
See: https://caniuse.com/css-subgrid.
You need to do it with <table>, <tr> and <td> elements and you can use colspan attribute for a full width cell.
First, im new so dont give me a hard time ;)
i have a problem with styling my foreach. What I want is that if I hover on the row. It only returns single line bottom and top border.
In this case when I hover on the next row the bottom top border from other rows are there creating a 2px border. I tried many things margin-top:-1px etc... some gave me a better result but not the final one.
.frameregels{
border-bottom:1px solid #e1e1e1;
border-top:1px solid #e1e1e1;
}
.frameregels:nth-child(odd){
background-color:#FFFFFF;
}
.frameregels:nth-child(even){
background-color:#f9f9f9;
}
.frameregels:hover{
background-color:#ecf5f9;
border-color:#66afe9;
}
<div class="xxlarge-12 xlarge-12 large-12 medium-12 small-12 columns frameregels">
<div class="xxlarge-1 xlarge-1 large-2 columns large-down-hidden">[artikelnr]</div>
<div class="xxlarge-11 xlarge-11 large-10 columns large-down-hidden">[omschrijving]</div>
</div>
<div class="xxlarge-12 xlarge-12 large-12 medium-12 small-12 columns frameregels">
<div class="xxlarge-1 xlarge-1 large-2 columns large-down-hidden">[artikelnr]</div>
<div class="xxlarge-11 xlarge-11 large-10 columns large-down-hidden">[omschrijving]</div>
</div>
<div class="xxlarge-12 xlarge-12 large-12 medium-12 small-12 columns frameregels">
<div class="xxlarge-1 xlarge-1 large-2 columns large-down-hidden">[artikelnr]</div>
<div class="xxlarge-11 xlarge-11 large-10 columns large-down-hidden">[omschrijving]</div>
</div>
margin-bottom:-1px is the completely right idea, however you need a few more things for that to work:
make sure you don't apply it to the last element to retain a clean bounding box of your list
position the elements in a way that accepts a z-index property so that you can shift the element you're hovering in the foreground. If you don't do this, you will change the color of your border, but the next element's border is just going to overlay it.
In practice, this is how it works (padding added for beauty):
.frameregels {
border-bottom: 1px solid #e1e1e1;
border-top: 1px solid #e1e1e1;
padding: 5px 0;
position: relative;
z-index: 0;
}
.frameregels:not(:last-child) {
margin-bottom: -1px;
}
.frameregels:nth-child(odd) {
background-color: #ffffff;
}
.frameregels:nth-child(even) {
background-color: #f9f9f9;
}
.frameregels:hover {
background-color: #ecf5f9;
border-color: #66afe9;
z-index: 1;
}
<div class="frameregels">
<div>[artikelnr]</div>
<div>[omschrijving]</div>
</div>
<div class="frameregels">
<div>[artikelnr]</div>
<div>[omschrijving]</div>
</div>
<div class="frameregels">
<div>[artikelnr]</div>
<div>[omschrijving]</div>
</div>
<div class="frameregels">
<div>[artikelnr]</div>
<div>[omschrijving]</div>
</div>