关于通过 CSS 在有或没有 JavaScript 帮助的情况下实现具有多个选项卡的单个 HTML 页面

About implement single HTML page with multiple tabs via CSS with or without JavaScript aid

我有兴趣构建一个包含一个 HTML 文件和多个选项卡的网页。

由于一次只能选择一个选项卡,我认为处理用户选择显示哪个选项卡的最合适方法是通过单选按钮,即用 <nav> 包裹 <ul> 包装一个 <li> 的列表,每个包装一个 <input> 后跟一个 <label> (另请参阅 )。

在搜索 on the web and 之后,我想出了这个:

function f(t) {
  // the vector below should be obtainable programmatically from the HTML
  ['tab1', 'tab2'].map(x => document.getElementById(x).style.setProperty('display', 'none'));
  document.getElementById(t).style.setProperty('display', 'initial');
}
ul {
  list-style-type: none;
}

/* With this ruleset I effectively target an HTML element,
   `label`, based on a condition being true or false about
   a different HTML element, `input`. The only relation
   between them is that they are siblings. */
input:checked + label {
  color: red;
}

/* I can't do the same with the element `#tab1` because it
   is not child nor sibling of the one holding the condition,
   the `input` targeted above. So I have to do this */
.tabs {
  display: none;
}
#tab2 {
  display: initial;
}
<nav>
  <ul class="site-nav">
    <li>
      <input id="tab1-radio" value="tab1" type="radio" name="nav-tabs" onclick="f(value)">
      <label for="tab1-radio" class="box-shadow">Tab 1</label>
    </li>
    <li>
      <input id="tab2-radio" value="tab2" type="radio" name="nav-tabs" onclick="f(value)" checked="checked">
      <label for="tab2-radio" class="box-shadow">Tab 2</label>
    </li>
  </ul>
</nav>
<div id="tab1" class="tabs">
Tab 1 content
</div>
<div id="tab2" class="tabs">
Tab 2 content
</div>

但是我看到了一些薄弱的(或讨论的)要点:


另一方面,another answer suggests a way to accomplish the task wihtout any JavaScript. I've also found a more thorough guide here,但这种技术依赖于手动 positioning 和通过 z-index 处理堆栈,但这开始看起来很困难,很麻烦,而且它给程序员带来了太多负担。

下面是我尝试简化该方法的尝试,但它失败了,如 linked article

中所述

not displaying any tab content if you link to the page without a hash selector, i.e. you link to mypage.html rather than mypage.html#tab1.

ul {
  list-style-type: none;
}

.page {
  display: none;
}

.page:target {
  display: initial;
}
<nav>
  <ul class="site-nav">
    <li>Click <a href="#one">here</a> for page 1</li>
    <li>Click <a href="#two">here</a> for page 2</li>
  </ul>
</nav>

  <div class="page" id="one">
    Tab 1 content
  </div>

  <div class="page" id="two">
Tab 2 content
  </div>

我认为它可以帮助你。

.content1,
.content2,
.content3 {
    display: none;
    padding: 20px;
    border-top: 2px solid #999999;
}

input[type='radio'] {
    width: 0;
    height: 0;
    opacity: 0;
}

label {
    cursor: pointer;
    display: inline-flex;
    justify-content: center;
    align-items: center;
    width: 80px;
    height: 30px;
    background-color: #dddddd;
    border-style: solid solid none solid;
    border-width: 2px;
    border-color: transparent;
}

#tab1:checked+label {
    border-color: #999999;
}

#tab2:checked+label {
    border-color: #999999;
}

#tab3:checked+label {
    border-color: #999999;
}

#tab1:checked~.content1 {
    display: block;
}

#tab2:checked~.content2 {
    display: block;
}

#tab3:checked~.content3 {
    display: block;
}
<div class="container">
    <input type="radio" name="tab" id="tab1" checked>
    <label for="tab1">content1</label>
    <input type="radio" name="tab" id="tab2">
    <label for="tab2">content2</label>
    <input type="radio" name="tab" id="tab3">
    <label for="tab3">content3</label>
    <div class="content1" id="content1">This is first content</div>
    <div class="content2" id="content2">This is second content</div>
    <div class="content3" id="content3">This is third content</div>
</div>

你可以这样写代码,但它不会工作,因为还没有引入 Selector4。

.content1,
.content2,
.content3 {
    display: none;
    padding: 20px;
    border-top: 2px solid #999999;
}

input[type='radio'] {
    width: 0;
    height: 0;
    opacity: 0;
}

label {
    display: inline-flex;
    justify-content: center;
    align-items: center;
    width: 80px;
    height: 30px;
    background-color: #dddddd;
    border-style: solid solid none solid;
    border-width: 2px;
    border-color: transparent;
}

#tab1:checked+label {
    border-color: #999999;
}

#tab2:checked+label {
    border-color: #999999;
}

#tab3:checked+label {
    border-color: #999999;
}

div:has(#tab1:checked)~div .content1 {
    display: block;
}

div:has(#tab2:checked)~div .content2 {
    display: block;
}

div:has(#tab3:checked)~div .content3 {
    display: block;
}
<div class="container">
    <div class="tab-container">
        <input type="radio" name="tab" id="tab1" checked><label for="tab1">content1</label>
        <input type="radio" name="tab" id="tab2"><label for="tab2">content2</label>
        <input type="radio" name="tab" id="tab3"><label for="tab3">content3</label>
    </div>
    <div class="content-container">
        <div class="content1" id="content1">This is first content</div>
        <div class="content2" id="content2">This is second content</div>
        <div class="content3" id="content3">This is third content</div>
    </div>
</div>

正如您在下面的 HTML 片段中看到的,我们为每个选项卡标题使用了简单的输入单选按钮字段和标签。然后我们将每个选项卡的内容添加到单独的 div class 名称 tab__content 中。每个输入 radio button 都有一个唯一的 ID,因此我们可以通过 CSS.

对其进行样式设置

body {
    font-family: 'cursive';
    background-color: #e7e7e7;
    color: #777;
    font-weight: 300;
}

.tab-wrap {
    -webkit-transition: 0.3s box-shadow ease;
    transition: 0.3s box-shadow ease;
    border-radius: 6px;
    max-width: 100%;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -ms-flex-wrap: wrap;
    flex-wrap: wrap;
    position: relative;
    list-style: none;
    background-color: #fff;
    margin: 40px 0;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
}

.tab-wrap:hover {
    box-shadow: 0 12px 23px rgba(0, 0, 0, 0.23), 0 10px 10px rgba(0, 0, 0, 0.19);
}

.tab {
    display: none;
}

.tab:checked:nth-of-type(1)~.tab__content:nth-of-type(1) {
    opacity: 1;
    -webkit-transition: 0.5s opacity ease-in, 0.8s -webkit-transform ease;
    transition: 0.5s opacity ease-in, 0.8s -webkit-transform ease;
    transition: 0.5s opacity ease-in, 0.8s transform ease;
    transition: 0.5s opacity ease-in, 0.8s transform ease, 0.8s -webkit-transform ease;
    position: relative;
    top: 0;
    z-index: 100;
    -webkit-transform: translateY(0px);
    transform: translateY(0px);
    text-shadow: 0 0 0;
}

.tab:checked:nth-of-type(2)~.tab__content:nth-of-type(2) {
    opacity: 1;
    -webkit-transition: 0.5s opacity ease-in, 0.8s -webkit-transform ease;
    transition: 0.5s opacity ease-in, 0.8s -webkit-transform ease;
    transition: 0.5s opacity ease-in, 0.8s transform ease;
    transition: 0.5s opacity ease-in, 0.8s transform ease, 0.8s -webkit-transform ease;
    position: relative;
    top: 0;
    z-index: 100;
    -webkit-transform: translateY(0px);
    transform: translateY(0px);
    text-shadow: 0 0 0;
}

.tab:checked:nth-of-type(3)~.tab__content:nth-of-type(3) {
    opacity: 1;
    -webkit-transition: 0.5s opacity ease-in, 0.8s -webkit-transform ease;
    transition: 0.5s opacity ease-in, 0.8s -webkit-transform ease;
    transition: 0.5s opacity ease-in, 0.8s transform ease;
    transition: 0.5s opacity ease-in, 0.8s transform ease, 0.8s -webkit-transform ease;
    position: relative;
    top: 0;
    z-index: 100;
    -webkit-transform: translateY(0px);
    transform: translateY(0px);
    text-shadow: 0 0 0;
}

.tab:checked:nth-of-type(4)~.tab__content:nth-of-type(4) {
    opacity: 1;
    -webkit-transition: 0.5s opacity ease-in, 0.8s -webkit-transform ease;
    transition: 0.5s opacity ease-in, 0.8s -webkit-transform ease;
    transition: 0.5s opacity ease-in, 0.8s transform ease;
    transition: 0.5s opacity ease-in, 0.8s transform ease, 0.8s -webkit-transform ease;
    position: relative;
    top: 0;
    z-index: 100;
    -webkit-transform: translateY(0px);
    transform: translateY(0px);
    text-shadow: 0 0 0;
}

.tab:checked:nth-of-type(5)~.tab__content:nth-of-type(5) {
    opacity: 1;
    -webkit-transition: 0.5s opacity ease-in, 0.8s -webkit-transform ease;
    transition: 0.5s opacity ease-in, 0.8s -webkit-transform ease;
    transition: 0.5s opacity ease-in, 0.8s transform ease;
    transition: 0.5s opacity ease-in, 0.8s transform ease, 0.8s -webkit-transform ease;
    position: relative;
    top: 0;
    z-index: 100;
    -webkit-transform: translateY(0px);
    transform: translateY(0px);
    text-shadow: 0 0 0;
}

.tab:first-of-type:not(:last-of-type)+label {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
}

.tab:not(:first-of-type):not(:last-of-type)+label {
    border-radius: 0;
}

.tab:last-of-type:not(:first-of-type)+label {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
}

.tab:checked+label {
    background-color: #fff;
    box-shadow: 0 -1px 0 #fff inset;
    cursor: default;
}

.tab:checked+label:hover {
    box-shadow: 0 -1px 0 #fff inset;
    background-color: #fff;
}

.tab+label {
    box-shadow: 0 -1px 0 #eee inset;
    border-radius: 6px 6px 0 0;
    cursor: pointer;
    display: block;
    text-decoration: none;
    color: #333;
    -webkit-box-flex: 3;
    -ms-flex-positive: 3;
    flex-grow: 3;
    text-align: center;
    background-color: #f2f2f2;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    text-align: center;
    -webkit-transition: 0.3s background-color ease, 0.3s box-shadow ease;
    transition: 0.3s background-color ease, 0.3s box-shadow ease;
    height: 50px;
    box-sizing: border-box;
    padding: 15px;
}

.tab+label:hover {
    background-color: #f9f9f9;
    box-shadow: 0 1px 0 #f4f4f4 inset;
}

.tab__content {
    padding: 10px 25px;
    background-color: transparent;
    position: absolute;
    width: 100%;
    z-index: -1;
    opacity: 0;
    left: 0;
    -webkit-transform: translateY(-3px);
    transform: translateY(-3px);
    border-radius: 6px;
}
<div class="tab-wrap">
   <!-- active tab on page load gets checked attribute -->
   <input type="radio" id="tab1" name="tabGroup1" class="tab" checked>
   <label for="tab1">Short</label>
   <input type="radio" id="tab2" name="tabGroup1" class="tab">
   <label for="tab2">Medium</label>
   <input type="radio" id="tab3" name="tabGroup1" class="tab">
   <label for="tab3">Long</label>
   <div class="tab__content">
      <h3>Short Section</h3>
      <p>Praesent nonummy mi in odio. Nullam accumsan lorem in dui. Vestibulum turpis sem, aliquet eget, lobortis pellentesque, rutrum eu, nisl. Nullam accumsan lorem in dui. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu.</p>
   </div>
   <div class="tab__content">
      <h3>Medium Section</h3>
      <p>Praesent nonummy mi in odio. Nullam accumsan lorem in dui. Vestibulum turpis sem, aliquet eget, lobortis pellentesque, rutrum eu, nisl. Nullam accumsan lorem in dui. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu.</p>
      <p>In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Morbi mattis ullamcorper velit. Pellentesque posuere. Etiam ut purus mattis mauris sodales aliquam. Praesent nec nisl a purus blandit viverra.</p>
   </div>
   <div class="tab__content">
      <h3>Long Section</h3>
      <p>Praesent nonummy mi in odio. Nullam accumsan lorem in dui. Vestibulum turpis sem, aliquet eget, lobortis pellentesque, rutrum eu, nisl. Nullam accumsan lorem in dui. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu.</p>
      <p>In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Morbi mattis ullamcorper velit. Pellentesque posuere. Etiam ut purus mattis mauris sodales aliquam. Praesent nec nisl a purus blandit viverra.</p>
      <p>Praesent nonummy mi in odio. Nullam accumsan lorem in dui. Vestibulum turpis sem, aliquet eget, lobortis pellentesque, rutrum eu, nisl. Nullam accumsan lorem in dui. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu.</p>
      <p>In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Morbi mattis ullamcorper velit. Pellentesque posuere. Etiam ut purus mattis mauris sodales aliquam. Praesent nec nisl a purus blandit viverra.</p>
   </div>
</div>

我不建议将此用于生产环境,我鼓励您使用适当的制表符模式,以便您可以操作相关的 WAI-ARIA 属性或 SPA 模式(导航发生后您将在何处管理焦点)等

但是话虽如此,我确实想表明使用 :target 选择器确实可以(具有初始内容),前提是您不介意 DOM 顺序是有点奇怪(应该不会有任何可访问性问题,因为其他部分已隐藏)。

我添加的“主页”实际上并不需要link,我只是为了完整性/给你选择而把它放在那里。

还要注意一些不寻常的事情 - 由于页面以这种方式更改,并不总是 <h1> - 我实际上不确定(我将不得不思考)如何最好地处理这个问题,但是对于现在我在每个部分添加了一个 <h1> 以便每个“页面”都有一个 <h1>.

ul {
  list-style-type: none;
}

.page{
   display: none;
} 

.page:target {
  display: initial;
}

.page:target ~ .initial  {
  display: none;
}
<nav>
  <ul class="site-nav">
   <li><a href="#home">home</a></li>
    <li><a href="#one">page 1</a> </li>
    <li><a href="#two">page 2</a> </li>
  </ul>
</nav>
<main>
  <section class="page" id="one" aria-labelledby="section1Heading">
    <h1 id="section1Heading">Tab 1 content</h1>
  </section>

   <section class="page" id="two" aria-labelledby="section2Heading">
    <h1 id="section2Heading">Tab 2 content</h1>
  </section>
  
  <section class="initial" id="home" aria-labelledby="homeHeading">
    <h1 id="homeHeading">Initial Page Content / Home Page</h1>
  </section>
  </main>