73 votes

Pourquoi jQuery n'explose-t-il pas si votre objet sélecteur n'est pas valide ?

J'ai récemment utilisé un code du type

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

Cependant, après un certain temps passé à essayer de le faire fonctionner, j'ai réalisé que mon sélecteur faisait référence à une division qui n'existait pas.

Le résultat de la requête était simplement qu'elle ne s'exécutait pas.

Il est évident qu'il s'agit d'un choix délibéré. Quelqu'un pourrait-il expliquer la logique de ce choix plutôt que de soulever une sorte d'exception ?

Je ne cherche pas à critiquer, mais à comprendre.

13 votes

D'accord, j'adore les questions de ce genre. "Voilà ce qui se passe quand je fais ça... pourquoi ?" est nettement plus intéressant que "C'est cassé, répare-le !".

0 votes

Si vous posez cette question sur la liste de diffusion de jquery, vous obtiendrez probablement des réponses plus informées à moins que jeresig ne soit ici :)

7 votes

56voto

Matt Sherman Points 3874

Pensez-y comme un requête ce qui est le cas. Vous demandez tous les "enregistrements" (éléments du DOM) qui correspondent à vos critères. Le résultat est un ensemble de zéro enregistrement.

Il passe ensuite en boucle sur vos enregistrements zéro et leur applique l'action. :)

Si vous faisiez la même chose avec SQL, ou un tableau, cela se comporterait de la même manière dans la plupart des langages. Une collection d'enregistrements nuls n'est pas un état d'erreur.

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 votes

L'absence d'un objet est aussi instructive que sa présence ;) Il faut juste concevoir un design pour cela.

0 votes

Ce que vous dites sur le fait de boucler sur un ensemble d'enregistrements a du sens pour les balises et les classes, mais pas pour les ID où il ne devrait y avoir qu'une seule instance. Je ne fais que le signaler.

54voto

Nick Craver Points 313913

Il y a quelques bonnes raisons à cela, la "chaînabilité" est le moteur principal, la capacité d'écrire du code très laconique par chaînage ne doit pas produire d'erreurs pour fonctionner sans problème, par exemple :

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

Chaque objet de la chaîne, même s'il ne fait référence à aucun élément du DOM, peut en avoir d'autres ajoutés ultérieurement, ou prenons un autre exemple :

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

Dans ce cas, nous ne nous soucions pas des éléments que le sélecteur a trouvés, mais du sélecteur lui-même. En voici un autre :

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

Même s'il n'y a pas d'enfants, nous pouvons vouloir revenir en arrière dans la chaîne par la suite, en continuant à utiliser l'élément .prevObject référence pour remonter la chaîne.

Il y a des dizaines de cas distincts comme celui-ci qui montrent les avantages de la bibliothèque telle qu'elle est. Quant à la pourquoi à partir d'interviews de John Resig qui est le créateur de jQuery, il déclare que c'est comme ça que ça s'est passé. Il cherchait un code aussi concis que possible, et le modèle de chaînage est ce qui en est ressorti. Il se trouve qu'il présente également de nombreux avantages, dont l'exemple ci-dessus.

Pour être clair, je ne dis pas chaque L'attribut de l'enchaînement est bon, il y a juste beaucoup d'avantages à cela.


Prenons cette page comme exemple, et si nous avions quelque chose comme ceci :

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

Devrait qui échoue parce qu'il n'y a pas encore de commentaires ? Eh bien non, pas vraiment, c'est prévu, je ne voudrais pas qu'une erreur se produise ici... si l'élément existe, faites-le, sinon, ne le faites pas. Ce que je veux dire, c'est que, du moins d'après mon expérience, no Lancer une erreur à cause d'un élément manquant est énormément plus utile que d'en lancer une.

Le sélecteur dans votre question, le #ID sélecteur est un cas très particulier où l'on ne s'attend qu'à un seul élément, donc on pourrait peut-être argumenter qu'il devrait échouer là... mais cela ne serait pas cohérent avec les autres sélecteurs, et on veut qu'une bibliothèque soit cohérente.

Avec à peu près n'importe quel autre sélecteur vous vous attendez à des éléments 0-many, donc échouer lorsque vous ne trouvez aucun élément serait nettement moins souhaitable dans la plupart des situations, et encore plus dans les cas tels que .live() ci-dessus.

0 votes

Le revers de la médaille est que, si vous voulez faire quelque chose dans le cas où un élément n'est pas présent, vous vous retrouvez avec de nombreuses vérifications pour les résultats de sélecteurs vides. Bien sûr, ce n'est pas ce que l'on veut faire. Si un élément d'une page est absent alors qu'il ne devrait pas l'être, la sortie de votre backend est erronée. Il est possible de détecter ce problème en testant la sortie pour certaines conditions.

9 votes

@Cthulhu - Si vous voulez vérifier, if(!$("selector").length) { } est un moyen assez court de le faire :) Vous pourriez également définir une méthode statique pour quelque chose comme ceci.

0 votes

Il peut être utile de mentionner le modèle de l'objet nul.

8voto

Frank Schwieterman Points 13519

C'est une question de flexibilité. Moi-même, j'aimerais les mêmes protections que vous demandez, vous pouvez toujours le faire vous-même. Utilisez :

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

    return this;
};

et utilisez maintenant $("input:checked").single() avec l'assurance qu'il renvoie un seul élément ou qu'il vous donne une erreur.

6voto

BGerrissen Points 9274

JQuery() retournera toujours un objet jQuery, d'une part pour éviter les erreurs, mais surtout :

Vous pouvez donc écrire du code réactif.

do X if Y is present

Si Y n'est pas présent, X n'est pas calculé.

Cela signifie que vous pouvez avoir un init global pour toutes les pages et juste init pour les plugins, qu'ils trouvent quelque chose ou pas.

$(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();
});

Mais bien sûr, certains plugins sont très mal écrits et provoquent une erreur.

5voto

jAndy Points 93076

Cela fait partie de la philosophie de jQuerys (en fait John Resigs, je suppose) concernant la bibliothèque.

Il essaie d'être "gentil" avec vous et vos clients, ce qui signifie qu'il ne fait que rarement des exceptions.
(comme très rarement)

Mais comme toujours, vous pouvez facilement l'étendre :

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

Mais comme Nick dit dans un commentaire, la plupart du temps c'est no un comportement souhaité. Si vous voulez quand même l'avoir pour une raison quelconque, un extrait de code comme celui ci-dessus devrait le faire.

2 votes

Avec une telle philosophie de développement, il devrait impérativement y avoir deux modes, un qui est indulgent (production) et un qui lance tôt et souvent (pour le débogage). Sinon, comment une personne saine d'esprit peut-elle faire du débogage ? Je ne connais pas du tout jQuery - peut-être qu'un tel mode existe ?

0 votes

@Konrad : Il suffit de mettre le code ci-dessus dans un fichier 'debug.js' et de l'inclure dans votre source uniquement pendant le développement. Ce genre de chose est un modèle courant.

2 votes

@Konrad : un sélecteur non apparié n'est pas, en soi, une erreur. Ce n'est pas comme, disons, HTML où il y a une norme (raisonnablement) stricte et des navigateurs indulgents qui ne forcent pas la conformité - les ensembles vides sont un comportement utile et fiable dans beaucoup (sinon la plupart) des programmes. Si votre programme risque de mal fonctionner si un sélecteur ne correspond pas, alors la charge de la preuve incombe à usted pour vérifier qu'il l'est.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X