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

Pei*_*wen 37 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)
});
Run Code Online (Sandbox Code Playgroud)

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

Xio*_*ion 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));
}
Run Code Online (Sandbox Code Playgroud)

否则,delay并且fadeIn#q13专门应用于元素,因为实际计数器(其最终值为13)将进入关闭状态.


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

将click处理程序直接注入循环的"自然"解决方案如下:

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

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

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

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

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

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

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

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


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

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

如果你已经意识到我试图在上面概述的闭包/范围问题,这可能是首选的解决方案.

  • `jQuery.each`一切都很好,但是我想在某些时候,每个JavaScript程序员都需要了解这里到底发生了什么.虽然对于这个特殊的例子它会做伎俩,一般来说它有自己的捕获(例如可能在`jQuery.each`"循环内外的`this`的不同指示"),你在使用它时应该记住. (5认同)
  • 不,它不会.将`var local`放在`for`循环中不会改变其范围.它仍然是**函数**中的本地,而不是循环块.JavaScript根本没有块范围.在浏览器的控制台中尝试以下简单示例,亲自查看:`a = []; for(var i = 0; i <2; ++ i){var local = i; a.push(function(){alert(local);}); 调用`a [0]()`和`a [1]()`将分别导致带有'1`的警报,而不是'0`和`1`. (5认同)
  • +1只回答照顾范围...... (4认同)
  • 你可以使用`jQuery.each`来自然地处理它 (3认同)
  • @Xion,只是使用工厂功能也很难解释它,它只是一种做同样事情的RY方式.我的评论是在您编辑帖子以进行解释之前做出的.`this`用于onclick处理程序,它与循环它的方式无关 - 一旦调用onclick,它将成为元素. (2认同)

lon*_*day 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>
Run Code Online (Sandbox Code Playgroud)

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

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

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

工作jsFiddle(我知道动画看起来有点笨重,抱歉.)

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

该代码可能不是非常快(如果你使用id的值,它几乎肯定会更快轻度),但您的标记更简单,因此更稳健.


Esa*_*ija 12

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

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

使用类和委派:

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

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

});
Run Code Online (Sandbox Code Playgroud)


Abd*_*nim 6

和其他人一样,你可以使用a for-loop来做到这一点,但是你实际上是将自己绑定到14 ps,每当你添加一个新的时候p你需要增加你的for-loop测试条件.

这就是我要做的:

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


Thi*_*ilo 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);
});
Run Code Online (Sandbox Code Playgroud)