英文:
Horizontal line after certain items no matter the visibilities
问题
我有一个由两种类型的项目组成的列表,这些项目始终是分组的,不会混在一起。就像这样:
a
a
a
b
b
b
我如何在`a`项目和`b`项目之间显示一个分隔线,即水平线?但是,如果某些项目具有`display: none`,该如何处理?
例如:
a
a
a
(在这里显示线)
b
b
b
a
a
a
b [display: none]
(在这里显示线)
b
b
a
a
(在这里显示线)
a [display: none]
b
b
b
是否可以仅使用CSS以一种不太繁琐的方式来实现这一点?
目前,我正在使用JavaScript手动执行此操作。
使用border-top或border-bottom在第一个/最后一个项目上不是解决方案,因为在应用样式时,样式会接触到边框,看起来不协调,我希望分隔线完全分开。我想使用伪元素,但不要使用position: absolute的技巧(除非它相对简单)。
我在JavaScript中的做法:
```js
App.check_pinline = () => {
App.remove_pinline()
let last_pinned
for (let item of App.get_items(`tabs`)) {
if (!item.visible) {
continue
}
if (item.pinned) {
last_pinned = item
}
else {
if (!last_pinned) {
return
}
else {
let pinline = DOM.create(`div`, `pinline`)
last_pinned.element.after(pinline)
return
}
}
}
}
<details>
<summary>英文:</summary>
I have a list comprised of 2 types of items, the items are always grouped, not mixed. Like:
a
a
a
b
b
b
How can I show a separator between the `a` items and the `b` items, a horizontal line. But how can I make that work even if some items have display: none.
For example:
a
a
a
(Show line here)
b
b
b
a
a
a
b [display: none]
(Show line here)
b
b
a
a
(Show line here)
a [display: none]
b
b
b
Is this possible with css only in a way that is not incredibly hacky?
For now I'm doing this manually with javascript.
Using border-top or border-bottom on first/last items is not a solution because when styled, the style touches the border, it looks off, I want the line to be completely separated. I guess a pseudo element, but not if it means using position: absolute hacks (unless it's pretty simple).
What I do with javascript:
```js
App.check_pinline = () => {
App.remove_pinline()
let last_pinned
for (let item of App.get_items(`tabs`)) {
if (!item.visible) {
continue
}
if (item.pinned) {
last_pinned = item
}
else {
if (!last_pinned) {
return
}
else {
let pinline = DOM.create(`div`, `pinline`)
last_pinned.element.after(pinline)
return
}
}
}
}
答案1
得分: 1
这高度依赖于您的HTML结构以及您如何隐藏元素(您没有在问题中包含它,所以这只是我的猜测)。我建议只在相同种类的最后一个可见元素上使用::after
伪元素。
如果display: none
是来自内联样式:
.a:not(:has(~ .a:not([style*='display: none']))):not([style*='display: none'])::after {
content: '';
height: 1px;
background: black;
width: 100%;
display: block;
}
case 1
<div>
<div class="a">a</div>
<div class="a">a</div>
<div class="a">a</div>
<div class="a">a</div>
<div class="b">b</div>
<div class="b">b</div>
<div class="b">b</div>
<div class="b">b</div>
</div>
<br>
case 2
<div>
<div class="a">a</div>
<div class="a">a</div>
<div class="a">a</div>
<div class="a">a</div>
<div class="b" style="display: none;">b</div>
<div class="b">b</div>
<div class="b">b</div>
<div class="b">b</div>
</div>
<br>
case 3
<div>
<div class="a">a</div>
<div class="a">a</div>
<div class="a">a</div>
<div class="a" style="display: none;">a</div>
<div class="b">b</div>
<div class="b">b</div>
<div class="b">b</div>
<div class="b">b</div>
</div>
英文:
This highly depends on the HTML structure that you have and how you hide the elements (you didn't include it in your question, so this is just my assumption). I'd suggest just use ::after
pseudo element on the last visible element of the same kind.
If the display: none
is from inline style:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-css -->
.a:not(:has(~ .a:not([style*='display: none']))):not([style*='display: none'])::after {
content: '';
height: 1px;
background: black;
width: 100%;
display: block;
}
<!-- language: lang-html -->
case 1
<div>
<div class="a">a</div>
<div class="a">a</div>
<div class="a">a</div>
<div class="a">a</div>
<div class="b">b</div>
<div class="b">b</div>
<div class="b">b</div>
<div class="b">b</div>
</div>
<br>
case 2
<div>
<div class="a">a</div>
<div class="a">a</div>
<div class="a">a</div>
<div class="a">a</div>
<div class="b" style="display: none;">b</div>
<div class="b">b</div>
<div class="b">b</div>
<div class="b">b</div>
</div>
<br>
case 3
<div>
<div class="a">a</div>
<div class="a">a</div>
<div class="a">a</div>
<div class="a" style="display: none;">a</div>
<div class="b">b</div>
<div class="b">b</div>
<div class="b">b</div>
<div class="b">b</div>
</div>
<!-- end snippet -->
The selector
.a:not(:has(~ .a:not([style*='display: none']))):not([style*='display: none'])
just means to select class a
that has no next siblings with class a
that has no style="display: none"
and it also has no style="display: none"
itself. If you hide the element with, for instance, .hidden
class, then you can change the [style*='display: none']
with .hidden
.
答案2
得分: 0
这是你提供的代码的翻译部分:
使用简单的`:not`和两个规则来解决问题(即,不使用现代的`:has`,该功能尚未在稳定版Firefox中实现)。
第一个规则将分隔符分配给所有可见的`.a`后面的任何可见的`.b`,然后第二个规则将该分隔符从任何可见的`.b`后面移除。为了演示,使用简单的`border-top: solid;`,使用类似`::before { content: ''; display: block; border-top: solid; }`的方式也可以达到相同的效果。
/* demo */
body { counter-reset: figs; }
figure { counter-increment: figs; counter-reset: as bs;}
figcaption::before { content: 'Fig 'counter(figs) ' - '; }
p { margin: 0; --m: 'visible';}
[hidden] { display: block !important; opacity: 0.3; --m: ' hidden';}
p::before { content: attr(class) '#'; }
.a { counter-increment: as; }
.b { counter-increment: bs; }
.a::after { content: counter(as) ' ' var(--m); }
.b::after { content: counter(bs) ' ' var(--m); }
<figure>
<figcaption>Everything visible:</figcaption>
<p class="a"></p>
<p class="a"></p>
<p class="a"></p>
<p class="b"></p>
<p class="b"></p>
<p class="b"></p>
</figure>
<!-- 省略部分HTML代码 -->
Using real `<hr>`, because, after all, it is probably the right element to use here.
另外,你提供的CSS代码也有一种更简单的写法,即使在“hidden”.b
上也保留分隔符:
.a:not([hidden]) ~ .b {
border-top: solid;
}
.b:not([hidden]) ~ .b {
border-top: none
}
如果你有任何其他需要翻译的内容,请继续提出。
英文:
It is solvable with simple :not
and two rules (i.e. without modern :has
that is yet about to be implemented in stable Firefox).
First rule that assigns separator to all visible .b
's following any visible .a
, and then second that takes that separator away from all .b
's after any visible .b
. For demonstrative purposes using simple border-top: solid;
-- using ::before { content: ''; display: block; border-top: solid; }
or similar instead would work the same.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-css -->
.a:not([hidden]) ~ .b:not([hidden]) {
border-top: solid;
}
.a:not([hidden]) ~ .b:not([hidden]) ~ .b {
border-top: none
}
/* demo */
body { counter-reset: figs; }
figure { counter-increment: figs; counter-reset: as bs;}
figcaption::before { content: 'Fig 'counter(figs) ' - '; }
p { margin: 0; --m: 'visible';}
[hidden] { display: block !important; opacity: 0.3; --m: ' hidden';}
p::before { content: attr(class) '#'; }
.a { counter-increment: as; }
.b { counter-increment: bs; }
.a::after { content: counter(as) ' ' var(--m); }
.b::after { content: counter(bs) ' ' var(--m); }
<!-- language: lang-html -->
<figure>
<figcaption>Everything visible:</figcaption>
<p class="a"></p>
<p class="a"></p>
<p class="a"></p>
<p class="b"></p>
<p class="b"></p>
<p class="b"></p>
</figure>
<figure>
<figcaption>First <code>.b</code> hidden:</figcaption>
<p class="a"></p>
<p class="a"></p>
<p class="a"></p>
<p class="b" hidden></p>
<p class="b"></p>
<p class="b"></p>
</figure>
<figure>
<figcaption>Last <code>.a</code> hidden:</figcaption>
<p class="a"></p>
<p class="a"></p>
<p class="a" hidden></p>
<p class="b"></p>
<p class="b"></p>
<p class="b"></p>
</figure>
<figure>
<figcaption>All but first and last hidden:</figcaption>
<p class="a"></p>
<p class="a" hidden></p>
<p class="a" hidden></p>
<p class="b" hidden></p>
<p class="b" hidden></p>
<p class="b"></p>
</figure>
<figure>
<figcaption>only <code>.a</code>'s visible</figcaption>
<p class="a" hidden></p>
<p class="a"></p>
<p class="a"></p>
<p class="b" hidden></p>
<p class="b" hidden></p>
<p class="b" hidden></p>
</figure>
<figure>
<figcaption>only <code>.b</code>'s visible</figcaption>
<p class="a" hidden></p>
<p class="a" hidden></p>
<p class="a" hidden></p>
<p class="b"></p>
<p class="b" hidden></p>
<p class="b"></p>
</figure>
<!-- end snippet -->
Could be written even more simple alternatively, leaving separators even on "hidden" .b
s:
.a:not([hidden]) ~ .b {
border-top: solid;
}
.b:not([hidden]) ~ .b {
border-top: none
}
Expanding this idea with flex
and order
to super simple pin (up) and filter items and separate them with separator if it make sense doodle:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
document.body.addEventListener(
'click',
function(e) {
const tgt = e.target;
if (tgt?.tagName != 'BUTTON') return;
tgt.parentElement.style.order *= -1;
}
);
function filter(str) {
document.querySelectorAll('span').forEach(
e => e[(e.textContent.includes(str) ? 'remove' : 'set') + 'Attribute']('hidden', ''));
};
<!-- language: lang-css -->
div {
display: flex;
flex-direction: column;
border: 1em inset;
}
hr {
display: none;
margin: 0;
border: 1em outset;
}
:not([hidden]):not([style^="order: -"])
~ :not([hidden])[style^="order: -"] ~ hr ,
:not([hidden])[style^="order: -"]
~ :not([hidden]):not([style^="order: -"]) ~ hr {
display: block !important;
}
<!-- language: lang-html -->
<input type="text" oninput="filter(this.value)" placeholder="zonk">
<div style="">
<span style="order: 1"><button>⇅</button> foo</span>
<span style="order: -1"><button>⇅</button> bar</span>
<span style="order: -1"><button>⇅</button> baaz</span>
<span style="order: 1"><button>⇅</button> gazonk</span>
<span style="order: 1"><button>⇅</button> qux</span>
<span style="order: 1"><button>⇅</button> quux</span>
<hr>
</div>
<!-- end snippet -->
Using real <hr>
, because, after all, it is probably the right element to use here.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论