在选定的类之间切换

5

我发现自己经常这样做:

$(document).on("click","li",function(){
    $(".selected").removeClass("selected"); // Remove any old selected
    $(this).addClass("selected"); // Apply selected to this element
});

有没有更好的方法来执行这样的任务,例如切换一个类。顺便说一下,每次只能选择一个元素。
谢谢。

2
toggleClass 能满足你的需求吗? - stuartd
从外观上看,这是一次代码审查,而不是代码出现了问题。 - Chris Dixon
@ChrisDixon 代码可以运行,但效率不高,很多用户要求使用特定方法的替代方案。 - Kivylius
@stuartd 不,我只是将removeClass()替换为toggleClass()。 - Kivylius
3
你的代码没问题,toggleClass不起作用是因为你只选中了一个元素。 - Rick Calder
5个回答

6
一种更有效的方法是跟踪上次选择的项目:
var $selected = null;

$(document).on("click", "li", function() {
    if ($selected) {
        $selected.removeClass('selected');
    }
    $selected = $(this).addClass('selected');
});

当然,只要该特定函数是唯一添加/删除selected类的函数,它就可以起作用。
这可以选择包装在闭包中以删除$selected变量。
顺便说一句,使用document作为委托的锚点不是最佳实践。最好选择最近不会从文档中移除的节点。
更新
正如Kevin B提到的那样,您可以这样消除分支:
var $selected = $();

$(document).on("click", "li", function() {
    $selected.removeClass('selected');
    $selected = $(this).addClass('selected');
});

$() 的使用能力是在 1.4 版本中引入的;在此之前,您需要使用 $([])


1
你也可以这样做:var $selected = $([]),然后移除if语句。 - Kevin B
@TecHunter 我不确定你所说的“更安全”是什么意思。答案已经明确说明了它适用的条件。 - Ja͢ck
@Jack 只是因为 OP 的方法肯定会从所有其他元素中删除类,而你的方法则假设它们永远不会超过一个选定的元素。我不是说这种情况会发生,但可能性是存在的。 - TecHunter
@TecHunter 这正是我回答的前提条件,所以这更像是一个事实而不是一个观点 :) - Ja͢ck
@Jack 没错,没有缓存,也没有管理,只是 DOM 检查。我只想提议一下对 OP 代码的小修改,用 $('.selected', myListWithOneOption) 代替 $('.selected')。这样搜索更快,少了些“亚优化”。 - TecHunter
显示剩余4条评论

2
你可以这样做:
<script>
  $(document).ready(function(){
     $(this).on("click", "li", function() {
        $(this).addClass('selected').siblings().removeClass('selected');
     });
  });
</script>

注意:仅当<li>不是动态添加的,并且它们都是兄弟节点时,此方法才有效。(这是常见情况,但不是唯一可能的情况。) - cHao
<ul><li>一项</ul><ul><li>另外一项,不是兄弟</ul>换行也会发生,这取决于 HTML 的样式。 - cHao
@cHao 你为什么关注那个?它与问题无关,而且那是原帖提供的代码示例。HTML不重要。 - Ian
1
@Ian:实际上,这与问题有很大关系。我专注于它,因为这段代码会以OP的代码不会出现的方式中断。它是否有效更多地取决于文档的结构(特别是所有<li>在单击<li>时的位置)。 - cHao
@cHao,这对于动态添加的项目也可以正常工作,因为.siblings()每次都会重新计算。 - Ja͢ck
显示剩余3条评论

1
思考一下,你可以将列表元素存储在变量中,例如:
var $liElements = $('#yourContainer > li');

$(document).on("click","li",function(){
    $liElements.not($(this)).removeClass("selected");
    $(this).addClass("selected");
});

缓存是一个不错的功能,但当项目动态添加时将无法使用。 - Ja͢ck
非常正确,但当项目在事件处理程序中动态添加时,它可能需要更新,每一点帮助都很重要。 - Chris Dixon
就缓存而言,var lis = document.getElementsByTagName('li');。返回值是实时的,这意味着任何添加到文档中的<li>元素也会神奇地出现在列表中。 - cHao

1

跟踪当前元素的概念与其他答案相同,但您可以将此逻辑整洁地封装在函数中,例如

function class_swapper=function(cls){
    var cur;
    return function(elt){
        if (cur) { cur.classList.remove(cls); }
        elt.classList.add(cls);
        cur=elt;
    };
};

调用 class_swapper 函数会返回一个函数,用于将指定的类应用到特定元素(并从先前的元素中删除该类,该元素在函数内部被记住)。您可以按以下方式使用它:

var swapper=class_swapper("selected");
swapper(elt1);
swapper(elt2);

或者以您的示例为例。
$(document).on("click","li",function(){swapper(this);});

我使用了classList.addclassList.remove,这是在现代浏览器中操作类的一种优雅的方式(哎呀),但当然可以根据需要用jQuery的addClass等来替换。


我喜欢封装这个想法,只要你只有一个像这样切换的组,它就可以与动态添加的元素一起工作。不过,风格注意:极度缩写的变量名很糟糕。 :P - cHao
有没有像 $(..).swapClasses(..) 这样的方法可以实现? - Kivylius
@Jessica,是的,你应该写一个jQuery插件。在这里查看:http://blog.teamtreehouse.com/writing-your-own-jquery-plugins - TecHunter

1

在与杰克辩论后,我提出我的建议。 假设你的列表在这里:

var $myList = $('#list');

然后:

$myList.on("click","li",function(){
    $(".selected",$myList).removeClass("selected"); // Remove any old selected
    $(this).addClass("selected"); // Apply selected to this element
});

或者

$myList.on("click","li",function(){
    $(this).siblings(".selected").removeClass("selected"); // Remove any old selected
    $(this).addClass("selected"); // Apply selected to this element
});

你的方法对我来说已经足够好了,但是Jack的方法更快,而我的方法介于两者之间。 我喜欢这种方法,因为不需要假设只有一个selected元素。据我所知,当我们提供上下文时,搜索会更快。

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