隐藏特定元素之前的所有元素

19

使用jQuery并在每个h1内的内容周围有一个包装容器,我可以轻松地隐藏它们。

但是如果没有包装容器,怎么办?

在不使用jQuery的情况下,如何最好地隐藏下一个h1之前的所有内容?

我没有使用jQuery,因为这是React应用程序的一部分。

h1 {
  border-bottom: solid 1px #000;
}

span {
  float: right;
}
<h1>h1 <span>x</span></h1>
<p>test 1</p>
<h2>h2</h2>
<p>test 2</p>

<h1>h1-1 <span>x</span></h1>
<p>test 3</p>
<h2>h2-2</h2>
<p>test 4</p>

或者将下一个h1标签之前和之后的所有内容包裹在一个div标签中?


如果子条目的数量非常有限,你可以直接使用+运算符:当需要展开时,添加一些类(例如expanded),然后重复相应次数即可:h1.expanded+p, h1.expanded+p+p, h1.expanded+p+p+p, ... { ... } - Fabian N.
6个回答

4

我可以用以下CSS来实现。只需将类名“start”添加到您想要开始折叠元素的h1中,将类名“upto”添加到您想要折叠元素停止的h1中。

CSS

.start ~ *:not(h1) {
   display: none;
}

.upto ~ * {
  display: block !important;
}

HTML

<h1 class='start'>h1 <span>x</span></h1>
 ...
<h1 class ='upto'>h1-1 <span>x</span></h1>

在此,从“start”到“upto”之间的所有元素都将被隐藏。您可以通过相应添加“start”和“upto”类来实现折叠效果。
也可以通过Javascript将类添加到下一个h1标签中。因此,我可以编写一个简单的js函数来设置“start”和“upto”。
function collapse(startHeaderNumber, uptoHeaderNumber) {
    var allHeaders = document.getElementsByTagName("h1");
    var totalNoOfHeaders = allHeaders.length;

   if (startHeaderNumber > totalNoOfHeaders) {
       return;
   }

   var startHeader = allHeaders[startHeaderNumber - 1];
   startHeader.classList.add('start');

   if (uptoHeaderNumber <= totalNoOfHeaders) {
      var uptoHeader = allHeaders[uptoHeaderNumber - 1];
      uptoHeader.classList.add('upto');
   }

}

而且您可以直接调用它

collapse(1 ,2)

它将会折叠在标题1和标题2之间的所有项目。或者,您可以这样调用它:

collapse(1)

这将折叠从第一个标题到最后一个标题之间的所有元素。

要查看完整演示,请参见此fiddle


2
听起来你想要类似于 jQuery 的 nextUntil() 函数。这里有一个使用原生 js 实现的好指南。最终的代码看起来像这样:
var nextUntil = function (elem, selector, filter) {

    // Setup siblings array
    var siblings = [];

    // Get the next sibling element
    elem = elem.nextElementSibling;

    // As long as a sibling exists
    while (elem) {

        // If we've reached our match, bail
        if (elem.matches(selector)) break;

        // If filtering by a selector, check if the sibling matches
        if (filter && !elem.matches(filter)) {
            elem = elem.nextElementSibling;
            continue;
        }

        // Otherwise, push it to the siblings array
        siblings.push(elem);

        // Get the next sibling element
        elem = elem.nextElementSibling;

    }

    return siblings;

};

我显然不知道你正在做什么的背景,但是我认为通过改变HTML有更好的方法来解决这个问题。这甚至可能让你只用css nth child selectors就能完成一些操作。

谢谢!这是一个目录组件。在移动端,我们希望用户能够折叠每个部分,但所见即所得编辑器不会创建包装容器,我们也不想在后端添加它。 - totalnoob

1
你可以使用previousElementSibling递归地获取h1元素的所有兄弟节点。这里是一个使用previousElementSibling的工作示例:

const elems = document.getElementsByTagName('h1');

for (let i = 0; i < elems.length; i++) hidePrev(elems[i]);

function hidePrev(elem)
{
    var pre = elem.previousElementSibling;
    if (!pre) return;
    pre.style.display = 'none';
    hidePrev(pre);
}
h1 {
  border-bottom: solid 1px #000;
}

span {
  float: right;
}
<h1>h1 <span>x</span></h1>
<p>test 1</p>
<h2>h2</h2>
<p>test 2</p>

<h1>h1-1 <span>x</span></h1>
<p>test 3</p>
<h2>h2-2</h2>
<p>test 4</p>


1

我已经对代码进行了注释。希望你能理解这里发生了什么,如果不能,请在下面评论。

function hideUntilNextSiblingWithSameName(elementName) {
  /*
   * @Param {elementName: String} 
   * Hide all elements until the next one with the same name
   */
  var trackH1 = 0,
      element = document.getElementsByTagName(elementName)[0],
      node = element.parentNode.firstChild;

  do {
    // Keep truck of element with the same name.
    if (node.tagName === element.tagName) trackH1 += 1;

    // Stop if catch element with same name
    if (trackH1 == 2) break;

    // Do not hide link, script, style and text node
    if (node.nodeType === 3 || node.tagName === "LINK" || node.tagName === "SCRIPT" || node.tagName === "STYLE") continue;
    // Hide element
    else {
      node.style.visibility = "hidden";
    }
  } while (node = node.nextSibling)
}

hideUntilNextSiblingWithSameName("h1")
<h1>h1 <span>x</span></h1>
<p>test 1</p>
<h2>h2</h2>
<p>test 2</p>

<h1>h1-1 <span>x</span></h1>
<p>test 3</p>
<h2>h2-2</h2>
<p>test 4</p>

<link rel="stylesheet" href="">
<script></script>
<style></style>

https://codepen.io/anon/pen/QxGVNG


目前,我隐藏了这些元素。但如果你想彻底不显示这些元素,请在else语句中使用node.style.display = "none";而不是node.style.visibility = "hidden";


0
您可以通过以下更简单的方式完成此操作。其思路是找到节点的直接父元素,例如在这种情况下是 body,并查找它的所有直接子元素。

然后,开始删除元素,直到找到所需的元素。在此示例中,我尝试隐藏元素,但您也可以简单地更改代码以将它们删除。

以下是一个可工作的演示:

const elems = document.querySelectorAll('body > *');
const elemBefore = document.querySelectorAll('h1')[1];

for (let i = 0; i < elems.length; i++) {
  let elem = elems[i];

  if (elem === elemBefore) {
    break;
  }

  elem.style.display = 'none';
}
h1 {
  border-bottom: solid 1px #000;
}

span {
  float: right;
}
<h1>h1 <span>x</span></h1>
<p>test 1</p>
<h2>h2</h2>
<p>test 2</p>

<h1>h1-1 <span>x</span></h1>
<p>test 3</p>
<h2>h2-2</h2>
<p>test 4</p>


0

Maybe you can use a trick and hide the

behind the h1 and h2 via css:

const selector = document.querySelectorAll('h1, h2');
const clickH = function (event) {
   const h = event.target;
   const attr = h.getAttribute('class');
   if (attr === '') {
      h.setAttribute('class', 'closed')
      return undefined;
   }
   h.setAttribute('class', '')
}
selector.forEach((h) => {
    h.setAttribute('class', 'closed')
    h.addEventListener('click', clickH);
})
h1, h2 {
  position: relative;
  border-bottom: solid 1px #000;
  background-color: #fff;
  height: 50px;
  border-bottom: solid 1px #000;
  display: block;
  z-index: 2;
  margin-bottom: 0;
}

.closed {
  margin-bottom: -50px;
}

p {
  height: 30px;
  
}

span {
  float: right;
}
<h1>h1 <span>x</span></h1>
<p>test 1</p>
<h2>h2</h2>
<p>test 2</p>

<h1>h1-1 <span>x</span></h1>
<p>test 3</p>
<h2>h2-2</h2>
<p>test 4</p>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接