在 Web 应用中,这是一个常见的需求:您点击某些内容,然后您刚刚点击的内容的文本就会发生变化。也许是像“显示”按钮切换到“隐藏”,或“展开描述”切换到“折叠描述”这样简单的事情。这是一件相当简单的事情,但需要考虑各种因素。让我们介绍一些方法。
jQuery 方法(更少的标记/更多的 JavaScript)
您需要将“交换”文本存储在某个位置。我认为在大多数情况下,这是一个设计/视图问题,因此将其存储在标记中是一个好主意。我们将使用一个按钮的示例,其文本在“隐藏”和“显示”之间切换。data-* 属性是存储交换文本的绝佳位置。因此,它变成了
<button data-text-swap="Show">Hide</button>
轻松替换文本,例如
var button = $("button");
button.text(button.data("text-swap"));
但是,如果我们这样做,我们将永远丢失原始文本。我们需要先存储原始文本。另一个 data-* 属性就可以做到。
var button = $("button");
button.data("text-original", button.text());
button.text(button.data("text-swap"));
要在点击事件中执行此操作,您需要执行以下操作
var button = $("button");
button.on("click", function() {
button.data("text-original", button.text());
button.text(button.data("text-swap"));
});
但这只向一个方向进行。为了完成“交换”,我们需要比较按钮的当前文本值是否与交换文本匹配。如果匹配,则将其更改回原始值。如果不匹配,则更改为交换文本。以下是完成后的样子
$("button").on("click", function() {
var el = $(this);
if (el.text() == el.data("text-swap")) {
el.text(el.data("text-original"));
} else {
el.data("text-original", el.text());
el.text(el.data("text-swap"));
}
});
jQuery 方法(更多的标记/更少的 JavaScript)
如果我们愿意在原始标记中设置该 data-text-original
值,我们可以简化 JavaScript 代码。我们可以使用单个三元运算符来检查交换是否与原始值匹配,并根据真值执行正确的操作。
$("button").on("click", function() {
var el = $(this);
el.text() == el.data("text-swap")
? el.text(el.data("text-original"))
: el.text(el.data("text-swap"));
});
原生 JavaScript 方法
我承认在这里使用了过多的 jQuery 来完成无需它就能完成的事情。以下是第一个“更少标记”版本在“原始”JavaScript 中的样子
var button = document.querySelectorAll("button")[0];
button.addEventListener('click', function() {
if (button.getAttribute("data-text-swap") == button.innerHTML) {
button.innerHTML = button.getAttribute("data-text-original");
} else {
button.setAttribute("data-text-original", button.innerHTML);
button.innerHTML = button.getAttribute("data-text-swap");
}
}, false);
CSS 方法(使用 jQuery 更改类名)
由于这是一个视图问题,可以被认为是一种“状态”,因此一个流行的想法是只使用 JavaScript 来更改表示状态的类,并让 CSS 定义视觉更改的实际内容。
我们可以使用类“on”来表示交换状态。然后,该类将应用一个伪元素,覆盖旧单词并将其替换为交换单词。我认为带有默认浏览器样式的实际按钮元素不太适合伪元素,因此让我们在这里使用一个锚点。
a {
position: relative;
}
a.on:after {
content: "Hide";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: white;
}
公平地说,这有点奇怪。我认为这几乎比将交换单词放在 JavaScript 中更糟糕。CSS 并不是真正为这种事情而设计的,并且可能存在一些无障碍性问题。
这也恰好有效,因为“隐藏”这个词比“显示”稍微小一点。如果交换词更大,则原始词将从白色覆盖层下方伸出来。您也许可以通过内联阻止原始词,隐藏溢出,并使用文本缩进将原始词踢出框外来解决此问题。但替换词是绝对定位的事实将其从流中移除,这可能是一个问题,更不用说现实世界中的设计并不总是像纯色对纯色那样简单。
纯 CSS 方法
但是,嘿,只要我们变得奇怪,我们就可以在这里使用 复选框技巧 来使文本完全通过 CSS 切换。替换方式完全相同,只是当单词前面的一个不可见的复选框处于 :checked
状态或未处于 :checked
状态时发生。这意味着单词也需要在标签中,该标签能够通过 for 属性切换该复选框的状态。
<input id="example-checkbox" type="checkbox">
<label for="example" id="example">Show</label>
#example {
position: relative;
}
#example-checkbox {
display: none;
}
#example-checkbox:checked + #example:after {
content: "Hide";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: white;
}
五种方法的演示
Check out this Pen!
更多?
您过去是如何处理这类事情的?我们没有介绍直接将交换单词放在 JavaScript 中……您对此有何看法?
不错的文章 Chris,我现在正在 CodePen 上使用它 :-)
如何使用 css 中的
attr()
从 html 获取数据而不是硬编码呢?—
不错 Subash,不错。
每当我交换内容时——即使是单个单词——我只使用 js 来切换类
和 css
更多的标记,更少的 js,但感觉更好:)
是的,使用
display: none;
我想它甚至不是一个无障碍性问题,是吗?这就是我更喜欢的方式(通常包装在锚点而不是 span 中)。这样,键盘用户可以选中它,屏幕阅读器将始终读取它当前显示的内容。更改类,屏幕阅读器现在在返回链接时读取新文本。
元素上的 data-* 属性也可以通过 element.dataset 访问,因此您也可以这样编写“原始”JavaScript 示例
当然,这在 IE < 11 中不起作用,但我们可以梦想 :(
与其将原始文本和要交换的文本分别存储在它们自己的数据属性中,并使用 if/else 逻辑来选择显示哪个,您只需使用一个数据属性来存储当前未显示的任何文本,然后交换值即可。所需的标记更少。
Peter,你证实了我刚才正在思考的事情。我投票给修改后的方法 #1 :)
绝对更好。除了我想它假设事件始终交换文本,无论是什么,而不测试状态。但如果我们考虑“假设”情况,我们需要变得复杂得多,所以这本身就很抽象了。
@Chris,没错。我只是在处理替换文本的任务,因为你最初的示例没有包含任何关于替换它的特定条件。该逻辑仍然可以添加,但理想情况下,你可能希望使用一个单独的变量来识别“状态”。如果你依赖于按钮的文本,如果你需要使用本地化的文本,则会增加复杂性。
我以前用原生JavaScript这样做。
@Chris 嗯,这与其他方法没有区别,而纯CSS方法虽然很好,但甚至不能比这做得更好:开/关,仅此而已。
谢谢,Peter!我在阅读文章时一直都在想同样的事情。这应该是切换事物的默认方式。
尽管如此,这是一个有趣的话题。我经常想知道是否有什么比用jQuery做它更简单的方法,而当我查看CSS解决方案时,答案似乎仍然是否定的。我找不到任何地方可以这么说:是的,这比用JS做要干净简洁得多。:(
在我看来有点奇怪——我们有那么多花哨的新元素,比如视频和特殊的输入字段,但仍然没有简单的切换元素。
这是原生版本
我在想Peter Foti建议的同样的解决方案。这是我的fiddle。
http://jsfiddle.net/rnavaneethan/Fax7D/1/embedded/result/
对于这个相当基本的示例来说,这将是过度杀伤,但由于它是更大Web应用程序的一部分,因此很有可能你应该已经在使用许多可用的客户端MV*框架之一。无论你是在Backbone中自己手动连接视图,还是使用为你绑定它们的框架(如Ember或Angular),将状态存储在模型中都更清晰。
只想说前两个JavaScript代码段缺少一个闭合括号
喜欢你的作品Chris。JavaScript的一个可能的快捷方式。
我想他最初使用的是getElementsByTagName,后来出于某种原因更改了它,但这并不是重点。
我认为我们可以将这一行理解为“使用更适合你的方法来获取那个该死的按钮元素!”
CSS第二种方式(法式风格)
input[type=checkbox].on-off{
appearance:none;
-webkit-appearance:none;
-moz-appearance:none;
}
input[type=checkbox].on-off:after{content:attr(data-unchecked);}
input[type=checkbox].on-off:checked:after{content:attr(data-checked);}
不错,但法式风格在哪里?
此外,如果你有信心使用:checked和appearance,你也应该使用::after(伪元素)而不是:after(伪类),因为我们仍然使用该语法的唯一原因是IE8……
这是最好的方法,不需要使用js,全部用css,也不需要在css中使用HTML代码。
我唯一要添加的是为复选框设置display: none,因为并非所有浏览器都支持appearance。
此外,我不会将input放在label内部,除此之外,这很棒。
<label><input class=”on-off” type=”checkbox” data-checked=”oui” data-unchecked=”non” /></label>
也许我只是有点奇怪,但通常我会将文本最初设置在任何我使用的HTML标签中,然后在需要时使用jQuery将该文本的内容替换为任何新文本。就像这样
$(“p”).text(“Hide”);
然后在必要时将其设置回显示
这样做有什么不好的原因吗?
不是因为你应该将内容保留在DOM中而不是你的代码中。为了保持简洁和分离,你知道的。
你的方法——传统的方法——绝对更简单、更快,并且使用更少的内存(但如果你担心在这样的任务中出现这些问题,你甚至不应该使用jQuery)。另一方面,当项目变得更大,并且你最终需要更改某些内容时,你可能会后悔你的决定。
假设你有两个切换按钮,但在另一个按钮中,你必须显示的单词例如是“开始”和“停止”:你必须为本质上相同的任务再次编写代码。
随意重复此操作,你就会明白。
@Mike,
可重用性和关注点分离。假设你有两个切换文本值的按钮,一个在Show/Hide之间切换,第二个在Forward/Backward之间切换。用你的方法,你必须为每个按钮编写两套JS,并在管理状态(开/关)的代码中关联View代码(即显示的文本)。
使用data属性或类似的东西,你的JS不需要知道文本是什么——只需要知道状态,以及打开或关闭它。
我正在我的Dreamweaver中尝试最后一个纯CSS版本。但它不起作用[chrome,firefox]。当我在codepen中执行相同操作时,它工作正常。我错过了什么?
CSS
HTML
< input id=”example-checkbox” type=”checkbox”>
< label for=”example” id=”example”>Show < /label >
@Surjith
for属性已更正
< label for=”example-checkbox” id=”example”>Show < /label >
谢谢Zet。
它起作用了。
帖子中给出的内容也有错误。
http://jsfiddle.net/vmJ5h/
我喜欢你使用数组为你做两件事的方式:),很聪明
你能解释一下代码中的数组部分吗:[ el.text(), el.text( el.data(‘swap’) ) ][0]
整个方法没有任何if-else语句,也不使用变量来存储当前文本值。我为此目的使用长度为2的数组。数组的第一个元素是当前文本值。第二个元素实际上交换了文本值,使用来自data属性的新值。由于我们已经将初始值存储为数组中的第一个元素,因此我们通过[0]立即访问它。我们用它来设置data属性的新值。
没有“if”,没有变量,只有两行代码。干净简洁。
嗨,感谢分享。我正在我目前正在进行的一个新项目中使用纯CSS的方式。
顺便说一句,我非常喜欢你的文章和教程。:)
我用过jQuery,但大多只是使用简单的原生JavaScript……
以下可以应用于任意数量的元素。
<button onclick=”elementStateChange(this, [“Show”,”Hide”])”>Show</button>
function elementStateChange(which, display){
if (which.innerText == display[0]){
which.innerText =display[1] ;
}else{
which.innerText =display[0] ;
}
}
Show
糟糕,应该出来……
<script>
function elementStateChange(which, display){
if (which.innerText == display[0]){
}
</script>
<button onclick=’elementStateChange(this, [“Show”,”Hide”])’>Show</button>
有用的信息chris。
@Paul
更紧凑
which.innerText = display[which.innerText == display[0] ? 1 : 0];
旧方法(从MS-IE4 / NN4到今天都有效)是
<input type=button value=Hide onclick="this.value = this.value == 'Hide' ? 'Show' : 'Hide';">
@Chris,@Peter
状态可以通过按钮中列出的内容来查看。但我不会真正推荐这样做,因为你将视图绑定到状态。
我脑子里有一个声音在说这是一个坏主意,但另一种方法是使用两个按钮。此HTML
此CSS
…以及以下jQuery
想法?
上面提到的Fiddle:http://jsfiddle.net/davidg707/JNB2F/
关于 CSS 方法(使用 jQuery 更改类名),您可以使用 text-indent,而无需使用背景颜色并在其上覆盖文本。
http://codepen.io/anon/pen/idmeG
为什么不切换类呢?通常会涉及一些 HTML 代码。数据属性并不总是足够的。
我在 Opera(Chromium 之前版本)中注意到 CSS 生成的内容的一个很酷的功能。
content
属性实际上对真正的元素起作用,而不仅仅是伪元素 before 和 after。当然,这在其他任何浏览器中都不起作用,我甚至不确定规范是否要求这样做。只是看到你的 :after 用法让我想起了这一点,仅此而已。 :)
我刚刚通过创建一个函数来完成此操作,该函数同时传递要检查状态的目标(大多数文本交换都是为此构建的)和要交换的文本,使用 jQuery 和原生 JavaScript 的混合方式。
然后像这样调用它
以及
想法?
我们需要在交换之前将数据存储到变量中。
我建议在 HTML 中使用一个没有文本的元素
以及这段 JS 代码使其生效
我为此创建了一个示例:cdpn.io/yEJdw
不错的方法,我喜欢 CSS 方法,但有时我们被迫使用其他方法。
如何在页面刷新时保留数据属性?