jQuery代码太冗长了,想指点如何缩短它

时间:2011-11-26 10:50:52

标签: javascript jquery

我有一些jQuery代码,我想要评论和指出如何减少和缩短其行数。

$('#p1').click(function() {
    $('#list').fadeOut(450);
    $('#q1').delay(600).fadeIn(450)
});
$('#p2').click(function() {
    $('#list').fadeOut(450);
    $('#q2').delay(600).fadeIn(450)
});
$('#p3').click(function() {
    $('#list').fadeOut(450);
    $('#q3').delay(600).fadeIn(450)
});
$('#p4').click(function() {
    $('#list').fadeOut(450);
    $('#q4').delay(600).fadeIn(450)
});

...

$('#p12').click(function() {
    $('#list').fadeOut(450);
    $('#q12').delay(600).fadeIn(450)
});
$('#p13').click(function() {
    $('#list').fadeOut(450);
    $('#q13').delay(600).fadeIn(450)
});

此代码可以更好地优化吗?或者至少减少冗长?

7 个答案:

答案 0 :(得分:68)

您可以使用for循环,但是应该确保循环计数器的值进入click事件处理程序的正确范围:

var clickHandler = function(k) {
    return function() {
        $('#list').fadeOut(450);
        $('#q' + k).delay(600).fadeIn(450);
    };
};
for (var i = 1; i < 14; ++i) {
    $('#p' + i).click(clickHandler(i));
}

否则delayfadeIn将专门应用于#q13元素,因为实际的计数器(最终值为13)将会关闭。


编辑:由于相当多的答案在这里弄错了,我会尝试更准确地解释这段代码中发生了什么,因为它似乎很混乱。

将点击处理程序直接注入循环的“自然”解决方案如下:

for(var i = 1; i < 14; i++) {
    $('#p'+i).click(function() {
        $('#list').fadeOut(450);
        $('#q'+i).delay(600).fadeIn(450)
    });
}

但这并不等同于扩展形式,扩展形式一个接一个地列出所有13种变体。问题是虽然这里确实创建了13个函数,但它们都在相同的变量i上关闭,它的值会发生变化。它最终到达13的值,循环结束。

一段时间之后,调用附加到#p1 ... #p13元素的函数(当单击其中一个元素时),并使用最终值i。这导致仅#q13被动画化。

这里需要做的是做一些名为lambda lifting的事情并消除自由变量i,其值无意中被改变。一种常见的技术是提供一个“工厂函数”,它接受变量的值并输出一个我们将用作事件处理程序的实际函数:

var clickHandler = function(k) {
    return function() {
        $('#list').fadeOut(450);
        $('#q' + k).delay(600).fadeIn(450);
    };
};

由于k参数的范围是clickHandler的本地范围,因此对clickHandler的每次调用都会得到不同的k变量。因此,从clickHandler返回的函数将关闭不同的变量,而这些变量又可以具有不同的值。这正是我们所需要的。然后我们可以从循环中调用clickHandler,并将计数器的值传递给它:

for (var i = 1; i < 14; ++i) {
    $('#p' + i).click(clickHandler(i));
}

我希望这会使区别变得更加清晰。


编辑:正如Esailija在评论中指出的那样,也可以使用jQuery.each来达到类似的效果:

$.each(new Array(13), function(idx) {
    $('#p' + (idx + 1)).click(function() {
        $('#list').fadeOut(450);
        $('#q' + idx).delay(600).fadeIn(450);
    });
});

如果你已经知道我上面试图概述的关闭/范围问题,这可能是首选的解决方案。

答案 1 :(得分:14)

与接受的答案相反,理想的解决方案IMO不是基于id值之间的关系建立代码,而是基于DOM中的关系。 jQuery为您提供了一个index方法,它允许您查看元素与其兄弟节点的关系。然后,您可以使用此值选择要显示的相应其他元素。

当然,这需要您以语义方式构建HTML。例如:

<div id="list">
    <a href="#">Link 1</a>
    <a href="#">Link 2</a>
    <a href="#">Link 3</a>
    <a href="#">Link 4</a>
    <a href="#">Link 5</a>
    <a href="#">Link 6</a>
    <a href="#">Link 7</a>
    <a href="#">Link 8</a>
</div>
<div id="questions"> <!-- perhaps this is what q stands for... -->
    <div>1 ...</div>
    <div>2 ...</div>
    <div>3 ...</div>
    <div>4 ...</div>
    <div>5 ...</div>
    <div>6 ...</div>
    <div>7 ...</div>
    <div>8 ...</div>
</div>

第一个链接适用于第一个嵌套div,第二个链接适用于第二个链接等。标记简单易读,语义清晰。

您的代码可以非常简单,无需担心工厂。事实上,最好的方法是使用事件冒泡和委托,由jQuery的on方法处理得令人钦佩(在1.7之前,使用delegate)。

$('#list').on('click', 'a', function() {
    $('#list').fadeOut(450);
    $('#questions div').eq($(this).index()).delay(600).fadeIn(450);
});

Working jsFiddle(我知道动画看起来有点笨重,对不起。)

$(this).index()找到当前元素与其兄弟姐妹的关系。 .eq()过滤选择(#questions div个元素)以找到相应位置的元素。

代码可能不是完全(如果你使用id值,它几乎肯定会更快一点),但你的标记更简单,因此更健壮。

答案 2 :(得分:12)

如果您必须坚持不使用事件委托和类,那么:

$.each( new Array(13), function(index){
    $('#p'+(index+1) ).click(function() {
        $('#list').fadeOut(450);
        $('#q'+(index+1)).delay(600).fadeIn(450)
    });
});

使用课程和授权:

$( document ).delegate( ".my-class", "click", function(){
var idIndex = this.id.match( /\d+$/, )[0];

        $('#list').fadeOut(450);
            $('#q'+idIndex).delay(600).fadeIn(450)

});

答案 3 :(得分:6)

与其他人一样,您可以使用for-loop来执行此操作,但实际上您只需要14 p,并且无论何时添加新的p,您都需要增加for-loop测试条件。

这就是我要做的事情:

$("[id^=p]").bind("click", function() {
    $('#list').fadeOut(450);
    $('#q' + $(this).attr("id").match(/^#p(\d+)$/i)[1]).delay(600).fadeIn(450)
})

答案 4 :(得分:5)

难道你不能只使用一个类而不是所有这些不同的ID吗?然后一些导航找到匹配的其他元素?

$('.myCoolEffect').click(function(event) {
    $('#list').fadeOut(450);
    // find the matching element based on this on somehow
    $(event.currentTarget).up(1).delay(600).fadeIn(450);
});

答案 5 :(得分:4)

试试这个,

$('[id^="p"]').live('click', function(e){
    var temp = this.id.match(/^p(\d+)$/);
    if(temp){
       $('#list').fadeOut(450);
       $('#q' + temp[1]).delay(600).fadeIn(450);
    }

});

我使用了jqueryStartsWith Selector&amp; live function

<强>更新

正如Xion所述,如果您使用此代码,则会影响其id'sp开头的所有其他元素。

如果你可以在id中添加更多字符,这不会有问题。

更新2:

作为评论中提到的BrunoLM,我稍微更改了代码,以便不会触发dom上以p开头的其他元素的事件。

var temp = this.id.match(/^p(\d+)$/);和以下if块会处理其idp开头的其他元素。

match(/^p(\d+)$/)返回null,其ID为pblabla, p2sdad, pdasds2323,并返回ID为p1, p2, ... p100000....

的数组

答案 6 :(得分:1)

$('div').filter(function () {
  return this.id.match(/^p([0-9]+)$/);
}).click(function () {
  $('#list').fadeOut(450);
  $('#' +$(this).attr('id').replace('p','q')).delay(600).fadeIn(450)
});