如果您的选择器对象无效,为什么jQuery不炸弹?


73

最近使用了一些类似的代码

$("#divMenuContainer:visible").hide("explode");

但是,花了一些时间尝试使其工作后,我才意识到我的选择器正在引用一个不存在的div。

查询的结果就是它没有执行。

显然,这是设计使然,有人能解释为什么选择这种设计而不是提出某种例外的逻辑吗?

不要试图批评只是试图理解。


12
同意,我喜欢这样的问题。“这是当我这样做时发生的事情……为什么?” 比“它坏了,解决它!”要有趣得多。
大卫2010年

可能在jquery邮件列表上询问此问题将获得更明智的答案..除非jeresig在这里:)
jrharshath 2010年


1
我不确定为什么,但是我确实很高兴!
乔什·斯托多拉

3
这就是为什么我写了这个小脚本:github.com/mikeycgto/jquery.whiny.js
mikeycgto 2011年

Answers:


53

这里有几个很好的理由,“可链接性”是主要驱动因素,通过链接编写非常简洁的代码的能力必须不会产生任何错误,例如,看起来很正常。

$("#divMenuContainer:visible").hide("explode").add("#another").fadeIn();

链中的每个对象,即使它没有引用DOM元素,以后也可能会添加更多,或者让我们来看另一个示例:

$("#divMenuContainer:visible").live("click", function() { ... });

在这种情况下,我们不在乎选择器找到的任何元素,我们在乎选择器本身。这是另一个:

$("#divMenuContainer:visible").find(".child").hide("explode").end().fadeOut();

即使没有孩子,我们也可能希望在之后跳回链中,继续使用.prevObject引用返回链中。

像这样的数十种不同情况表明了图书馆的现状。至于为什么,在jQuery的创建者John Resig的采访中,他说这就是如何实现的。他尽可能地精简代码,而链接模型就是其中之一,它恰好也有很多好处,上面的例子只是其中的一些。

明确地说,我并不是说链接的每个属性都是一个好属性,它有很多好处。


让我们以该页面为例,如果我们遇到这样的情况:

$(".comment").click(replyToFunction);

是否应该因为没有任何评论而失败?好吧,不是真的,这是预料之中的,我不想在这里出现错误...如果该元素存在,那么就这样做,否则就不要。我的观点是,至少就我的经验而言,不要因为缺少元素抛出错误比抛出一个错误要有用得多。

问题中的#ID选择器是一个非常特殊的情况,您只希望有一个元素,所以也许您可能会争辩说它应该在那里失败...但是那样就不会与其他选择器保持一致,并且您需要一个库保持一致。

对于几乎所有其他选择器,您都期望0个元素。因此,在大多数情况下,如果找不到任何元素,则失败的可能性将大大降低,在上述情况下甚至更是如此.live()


这枚硬币的缺点是,如果您想在不存在元素的情况下执行某些操作,那么您将有很多检查以了解空的选择器结果。当然,不想这样做。如果页面上的元素不应该丢失,那么后端的输出是错误的。这可以通过在某些条件下测试输出来捕获。
cthulhu

9
@Cthulhu-如果要检查,这if(!$("selector").length) { }是一种很短的方法:)您还可以为类似的东西定义一个静态方法。
尼克·克拉弗

提及空对象模式可能很有用。
马特·鲍尔

@Bears-对于某些示例,它适合此模式,对于其他示例,例如.live()我们甚至不在乎内部的DOM元素...不是列表为null或为空,我们只是不在乎。所以有些事情链接使用它,有的没有,有时他们使用其他属性(.selector.context在这种情况下,.prevObject在其他)。
尼克·克拉弗

3
@sfink-当然,如果您愿意,它看起来像这样:jQuery.fn.require = ​function(num, num2) { if(this.length < num) $.error("At least " + num + " elements are required"); if(num2 && this.length > num2) $.error("Between " + num + " and " + num2 + " elements required"); };您可以在此处进行测试/播放:jsfiddle.net/nick_craver/VrXmc检查控制台是否有错误。
尼克·克雷弗

56

可以将它视为一个查询。您要查询所有符合条件的“记录”(DOM元素)。结果是一组零记录。

然后,它将遍历您的零记录并将操作应用于它们。:)

如果您对SQL或数组执行相同的操作,则在大多数语言中,其行为将相同。零记录的收集不是错误状态。

var things = $("invalid selector");
$("p").text("The object is valid: " + things + " but has " + things.length + " elements.")
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<p></p>


6
缺少对象就像它的存在一样有意义;)只需为此进行设计。
dmp 2010年

您说的关于遍历一组记录的内容对于标记和类是有意义的,但对于仅应有一个实例的ID则没有意义。只是指出这一点。
格里2012年

8

这是灵活性的问题。我自己想要与您一样的保护,您随时可以自己做。采用:

jQuery.fn.single = function() {
    if (this.length != 1) {
        throw new Error("Expected 1 matching element, found " + this.length);
    }

    return this;
};

现在使用$(“ input:checked”)。single(),以确保其返回单个项目或给您一个错误。


6

jQuery()将始终返回jQuery对象,以防止错误,但更重要的是:

因此,您可以编写响应式代码。

do X if Y is present

如果不存在Y,则不计算X。

这意味着您可以拥有一个全局的初始化跨页,而无论是否找到东西,都可以使用初始化插件。

$(function(){
    // does nothing if the page contains no element with className 'accordion'
    $('.accordion').implementAccordion();
    // usually on a single page, but we can add it to a global js file nontheless.
    $('.validate-form').implementFormValidator();
});

当然,有些插件的编写确实很差,并且会引发错误。


5

它是关于库的jQuery(实际上是John Resigs)哲学的一部分。

它试图对您和您的客户“友好”,这反过来意味着,它很少会抛出异常
(真的很少)

但是像往常一样,您可以轻松地扩展它,例如:

(function(_jQuery){
    jQuery = function(){
        var ret = _jQuery.apply(this, arguments);
        if(!ret.length) throw new Error('empty selector');
        return ret;
    };
}(jQuery));

但是就像Nick在评论中说的那样,大多数情况下这不是期望的行为。无论如何,如果您出于某种原因想要使用它,可以使用上面的代码段来实现。


2
有了这样的开发理念,就必须有两种模式,一种是宽容(生产)模式,另一种是早期且经常抛出的模式(用于调试)。否则,一个理智的人如何进行调试?我完全不了解jQuery –也许存在这种模式?
Konrad Rudolph 2010年

@Konrad:只需将上面的代码放入文件“ debug.js”,并仅在开发期间将其包括在您的源代码中即可。这种事情是常见的模式。
rfunduk 2010年

2
@Konrad:一个不匹配的选择器本身并不是错误。就像HTML并不存在(合理地)严格的标准和宽松的浏览器,它们不会强制遵守规定-空集是许多(如果不是大多数)程序中有用且依赖的行为。如果,如果选择不匹配,你的程序将发生故障,那么责任在验证它是。
Shog9 2010年

@ Shog9:这不是问题。问题是:为什么该死的不起作用?……x次以后:哦,该死,我有一个字符的拼写错误,而我的选择器实际上没有匹配任何内容。或者我不小心在HTML中设置了一个类而不是ID,并且一直以来我都试图匹配一个ID。
格里2012年

3

没有引用任何元素的选择器仍然是合法选择器,并且可能是故意的。给定的选择器有时可能会返回元素,并且您希望能够使用这样的选择器而不会引发运行时错误。


3

一个很好的例子是当您想对所有选中的复选框进行操作时

$("input:checked")

...您不知道要检查多少。可以是任何,全部或没有。这取决于用户。

因此,不必像

var checkedInputs = $("input:checked");
if (checkedInputs  && checkedInputs .length > 0) {
      checkedInputs .doStuff();
}

你可以

$("input:checked").doStuff();

如果他们做出选择,那就太好了,事情就完成了。如果没有...没有伤害,没有犯规。


1
仅供参考-if()语句中的第一个测试将始终等于true,因此您可以执行以下操作:if(checkedInputs.length > 0) {...。或者,由于任何大于0等于的数字true,您都可以将其降低到if(checkedInputs.length) {...。:o)
user113716

2

因为$("#thisdivdoesntexist")在jQuery中仍然返回“空” jQuery,Object并且所有jQuery对象都有其方法,所以没有错误。

这实际上是一件好事。如果这将引发错误,则在执行某些操作之前,您将需要在很多情况下进行一些额外的检查,这意味着您将有很多“重载”代码。虽然它不会是不好的做法,以检查它是否调用对象的方法,当选择回报什么,不是所有的JavaScript将暂停(即如果它会抛出一个错误会发生什么)之前就存在

因此,即使某些页面没有选择器,您也可以全局使用选择器,而不必担心。


是的,$('something')始终返回一个jQuery对象,如果为$("#thisdivdoesntexist"),则该对象包含0个元素。
火箭Hazmat

无论如何,当然这是对的调用jQuerys constructor function,但这可能至少应该有选择地发出警告。
jAndy 2010年

您将如何发出警告?
Stefanvds 2010年

1

通常,只有在元素存在时,我才继续执行以下操作:

var aThing = $("#myElement");
if(aThing.length){
    //my code here
}

0

我认为这可能与您的选择器有关:

$("#divMenuContainer:visible")

在幕后将返回一个jQuery对象,其中包含许多可能的匹配项。然后,对每个功能执行“隐藏”功能。我想在这种情况下不抛出异常在某种意义上是可行的,因为您有一个包含零个条目的列表,而不是返回空值。


0

这对我来说很有意义...如果您的选择器不匹配任何元素,您仍然可以调用所有常规jQuery原型函数而不会产生错误!最坏的情况是,您最终得到一个“空” jQuery集,将您的更改应用于任何元素。


0

我猜是因为它将在前端使用-最终用户不应看到异常,因为开发人员编写了一些错误的代码。通常,在这种情况下静默失败可能更安全。


0

我认为这就像CSS,这意味着如果您尝试设置不存在的元素的样式,则不会出错。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.