点击 legend 元素时折叠 fieldset。

15

我有一个 jQuery 函数,它可以通过单击 fieldset 的 legend 来切换其内容的可见性,仅留下 fieldset 边框(如果有)和 legend 显示:

$('legend.togvis').click(function() {
    $(this).siblings().toggle();
    return false;
});

除非fieldset包含文本节点,否则它运行得很好。

<fieldset><legend class='togvis'>Click Me</legend>
  <p>I toggle when the legend is clicked.</p>
  But I'm a recalcitrant text node and refuse to toggle.
</fieldset>

为了同时切换文本节点,我尝试了这个:

$('legend.togvis').click(function() {
    $(this).parent().contents().not('legend').toggle();
    return false;
});

这个函数的作用与第一个函数相同。还有这个:

$('legend.togvis').click(function() {
    $(this).parent().contents(":not(legend)").toggle();
    return false;
});

导致错误的代码

Message: 'style.display' is null or not an object
Line: 5557
Char: 3
Code: 0
URI: http://code.jquery.com/jquery-1.4.4.js

如何在单击图例时获取整个字段集(除传说之外)的全部内容?

ETA解决方案,非常感谢Eibx

$('legend.togvis').click(function() { 
    $(this).parent().contents().filter(
        function() {  return this.nodeType == 3; }).wrap('<span></span>');//wrap any stray text nodes
    $(this).siblings().toggle(); 
}); 

看起来问题在于文本节点不能有样式。由于.toggle()通过影响节点的CSS来工作,它无法影响文本节点。也许我需要将fieldset的内容存储在.data()中,然后删除子元素? - dnagirl
7个回答

16

在HTML、JavaScript或CSS中,你无法让文本消失。它需要被包含在一个HTML元素中,并应用display:none;或类似的属性。

以下代码将把所有内容封装到一个div中,并将您的图例移动到与div相同的级别,并在单击图例时显示/隐藏div。

$("fieldset legend").click(function() {
  if ($(this).parent().children().length == 2)
    $(this).parent().find("div").toggle();
  else
  {
    $(this).parent().wrapInner("<div>");
    $(this).appendTo($(this).parent().parent());
    $(this).parent().find("div").toggle();
  }
});

谢谢,但我建议您将一些代码移动到$(document).ready()中,因为这样可以在每次单击时检查元素。 确保所有图例都有正确的子元素是明智的做法。 可以参考以下示例:http://jsbin.com/eleva3/2/edit - はると
不幸的是,它引起了一些布局问题。不过你正在给我指明一个非常好的方向。 - dnagirl
很遗憾,这是我能做到的最接近你的设计而不破坏它的方式:http://jsbin.com/eleva3/3/edit - 但是有很多缺陷,我不建议你使用它。 - はると
提醒所有使用此功能的人,注意一下。在IE8(可能还有更早版本)中,如果将DIV元素呈现在LEGEND元素之上,它实际上会出现在图例元素之上。您需要调整代码以使div元素出现在图例元素下方(不能对所有内容都调用wrap inner)。 - Sean Anderson
这可能有点老了,但我认为值得一提的是,除了使用appendTo之外,我发现使用children更有帮助。 - drs9222
显示剩余2条评论

11

由于文本节点不是元素,因此无法对其进行切换。

作为最后的办法,如果您无法使用元素包装它们,则可以删除除图例以外的所有内容,然后稍后再添加元素和文本节点:

$(document).ready(function() {
    $('legend.togvis').click(function() {
        var $this = $(this);
        var parent = $this.parent();
        var contents = parent.contents().not(this);
        if (contents.length > 0) {
            $this.data("contents", contents.remove());
        } else {
            $this.data("contents").appendTo(parent);
        }
        return false;
    });
});

你可以在这里测试该解决方案


这对我有用。我想能够切换一个带有报告筛选控件的字段集,因此我有几个下拉框和几个文本框及其相应的标签。 - Nick Harrison

11

光荣的纯HTML/CSS解决方案

现在在2019年,这可以完全通过HTML和CSS实现。

#toggle + #content {
    display: none;
}

#toggle:checked + #content {
    display:block;
}

legend {
    width: 85%;
    border: 1px solid black;
    padding: 3px 6px;
    cursor: pointer;
}

legend label {
    width: 100%;
    display: inline-block;
    cursor: inherit;
}

legend label::after {
    content: "▼";
    float: right;
}
<body>
<form>
<fieldset>
    <legend><label for="toggle">Test</label></legend>
    <!-- This input has no name, so it's not submitted with the form -->
    <input type="checkbox" id="toggle" checked="" hidden=""/>
    <div id="content">
        <label>Some text field<input type="text" name="some-text-field"/></label><br/>
        <label>Some color input<input type="color" name="some-color-field"/></label>
    </div>
</fieldset>
</form>
</body>

Javascript版本

尽管纯HTML / CSS解决方案显然很好,但在页面上使用Javascript编程实现交互也没有问题。但我注意到所有其他答案都使用JQuery,在2019年完全是不必要的。

window.addEventListener('load', () => {
    const collapser = document.getElementById('collapser');
    const collapsable = document.getElementById('collapsable');

    function collapse(event) {
        if (event) {
            event.stopPropagation();
        }

        collapsable.hidden = !collapsable.hidden;
    }

    collapser.addEventListener('click', collapse);
});
legend {
    width: 85%;
    border: 1px solid black;
    padding: 3px 6px;
    cursor: pointer;
    display: inline-block;
}

legend::after {
    content: "▼";
    float: right;
}
<body>
<form>
<fieldset>
    <legend id="collapser">Test</legend>
    <div id="collapsable">
        <label>Some text field<input type="text" name="some-text-field"/></label><br/>
        <label>Some color input<input type="color" name="some-color-field"/></label>
    </div>
</fieldset>
</form>
</body>


我不想为箭头制作过渡动画,但是使用JavaScript可以完全实现(尽管在没有 JavaScript 的情况下更难)。如果您的支持浏览器都符合HTML5标准,您也可以使用<details> / <summary> 来达到类似的效果。 - ocket8888
Safari不支持“hidden”属性。 - Donald Winston
Safari绝对支持“hidden”全局属性(https://caniuse.com/?search=hidden)。它不支持该属性的“until-found”关键字值,但我在这里没有使用它。 - ocket8888

6
使用div标签来分离内容,例如:
<fieldset>
  <legend class='togvis'>Click Me</legend>
  <div class="contents">
  <p>I toggle when the legend is clicked.</p>
  But I'm a recalcitrant text node and refuse to toggle.
  </div>
</fieldset>

那么你只需要切换div即可。
$('legend.togvis').click(function() {
    $(this).closest("contents").toggle();
    return false;
});

更新:只有在您无法编辑HTML的情况下,才可以执行以下操作但仅当所有文本都在至少一个标记内时才有效

$('legend.togvis').click(function() {
    $(this).parents("fieldset").find("*:not('legend')").toggle();
    return false;
});

在线演示请点击: http://jsfiddle.net/yv6zB/1/

他可能无法做到那件事。 - hunter
你更新的代码不会影响文本节点。它只是执行了我的原始函数所做的事情。我曾考虑过使用内部div作为切换钩子,但添加非语义标记仅为了获得JavaScript功能会冒犯我的感官。;) - dnagirl
明白了,但是文本节点怎么可能漂浮呢?难道这个“但我是一个顽固的文本节点,拒绝切换。”不应该放在段落标签里面吗? - amosrivera

1

也许你只需要将文本放入sibilig标签中:

<fieldset><legend class='togvis'>Click Me</legend>
  <div><p>I toggle when the legend is clicked.</p>
  But I'm a recalcitrant text node and refuse to toggle.</div>
</fieldset>

他可能无法做到那件事。 - hunter

0

我喜欢被接受的答案,但是发现它对我的目的来说不够强大。仅仅检查子元素数量的长度并不能支持许多场景。因此,我会考虑使用类似于以下实现的方法:

$("fieldset legend").click(function () {
    var fieldset = $(this).parent();
    var isWrappedInDiv = $(fieldset.children()[0]).is('div');

    if (isWrappedInDiv) {
        fieldset.find("div").slideToggle();
    } else {
        fieldset.wrapInner("<div>");
        $(this).appendTo($(this).parent().parent());
        fieldset.find("div").slideToggle();
    }
});

0

修改ocket8888的js答案以适用于类

window.addEventListener('load', () => {
    function collapse(event) {
        if (event) {
            event.stopPropagation();
        }

        var targetdiv = event.target.parentElement.getElementsByClassName("collapsable")[0];
        targetdiv.hidden = !targetdiv.hidden;
    }

    var collapser = document.getElementsByClassName('collapser');
    Array.from(collapser).forEach((collapse_tag) => {
        collapse_tag.addEventListener('click', collapse);
    });
});
.collapsable {
     border: 1px solid #F00;
}
legend {
    width: 85%;
    border: 1px solid #0F0;
    padding: 3px 6px;
    cursor: pointer;
    display: inline-block;
}

legend::after {
    content: "▼";
    float: right;
}
<body>
<form>
Above
<fieldset>
    <legend class="collapser">Test</legend>
    <div class="collapsable">
        <label>Some text field<input type="text" name="some-text-field"/></label><br/>
        <label>Some color input<input type="color" name="some-color-field"/></label>
    </div>
</fieldset>
Below
</form>
</body>


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