2009-11-06 8 views
1

Le script suivant active/désactive les contrôles de formulaire imbriqués en utilisant un nom de classe for:x. Le problème est que les éléments for:y imbriqués sont activés alors qu'ils ne le devraient pas. Ils ne doivent être activés que lorsque le for:y imbriqué est activé.exclure les enfants imbriqués

Quelqu'un peut-il me fournir un filtre supplémentaire pour exclure les éléments de formulaire qui se trouvent dans une classe for:* imbriquée?

En d'autres termes, étant donné l'exemple ci-dessous, lorsque losssold est cochée, toutes les entrées de l'enfant de for:losssold doivent être activés à l'exception des éléments d'élément enfant à l'intérieur for:has-buy-sell-agreementtrue si has-buy-sell-agreementtrue est pas également vérifié.

Donc, je pensais qu'il serait plus facile de simplement exclure ces éléments dans for:has-buy-sell-agreementtrue.

<script type="text/javascript"> 
$('[class*="for:"]').each(function(){ 
    var klass=$(this).attr('class'),element; 
    var that = this; 
    $.each(klass.split(' '),function(index,value){ 
     if(!value)return; 
     if (value.substr(0,4)=='for:') 
      element=value.substr(4); 
    }); 
    $('[name="' + $('#'+element).attr('name') + '"]') 
     .click(function(){ 
      $(that).toggleClass('disabled', !$('#'+element).is(':checked')) 
       .find('input,select,textarea') /* insert edge case here */ 
       .each(function(){ 
        if ($('#'+element).is(':checked')) 
         $(this).removeAttr('disabled'); 
        else 
         $(this).attr('disabled','disabled'); 
       }); 
     }) 
     .trigger('click'); 
}); 
</script> 

    <li>What would you like to see happen to your business when you (or a co-owner) die, become disabled, or retire? <br/> 
    <ul><li><label><input type="radio" name="loss" id="losssold" value="sold"/> Sold to other Owners/another Business</label> 
     <ul class="for:losssold"> 
      <li>Do you have a buy-sell agreement?<br/> 
      <label><input type="radio" name="has-buy-sell-agreement" id="has-buy-sell-agreementtrue" value="true"/> Yes</label> 
      <label><input type="radio" name="has-buy-sell-agreement" id="has-buy-sell-agreementfalse" value="false"/> No</label><br/> 
      <ol class="for:has-buy-sell-agreementtrue"> <!-- nested for class; inner elements shouldn't be affected --> 
       <li><label for="buy-sell-last-updated">When was the agreement last updated?</label><br/> 
       <input type="text" name="buy-sell-last-updated" id="buy-sell-last-updated" value=""/> 
       </li> 
       <li>Is the agreement funded?<br/> 
       <label><input type="radio" name="buy-sell-is-funded" id="buy-sell-is-fundedtrue" value="true"/> Yes</label> 
       <label><input type="radio" name="buy-sell-is-funded" id="buy-sell-is-fundedfalse" value="false"/> No</label> 
       </li> 
      </ol> 
      </li> 
     </ul> 
     </li> 
    </ul> 
    </li> 
+0

Pourriez-vous préciser la question? 'Le problème est que les elts imbriqués sont activés. Elles ne devraient être activées que si elles sont imbriquées. Voulez-vous dire: xxx elt doit être activé uniquement s'il s'agit de children fox: xxx elts sont activés? –

+0

@ culebrón J'ai essayé de clarifier en éditant mon post, j'espère que cela aide – Dave

Répondre

0

J'ai fini par résoudre ce problème en analysant récursivement les éléments for^= enfants lorsque l'événement a été déclenché.

$(function(){ 
    $('[class^="for:"]').each(function(){ 
     var that=this; 
     function getElement(forElement){ 
      var klass=$(forElement).attr('class'),element; 
      $.each(klass.split(' '),function(index,value){ 
       if(!value)return; 
       if(value.substr(0,4)=='for:'){ 
        element=value.substr(4); 
        return false; 
       } 
      }); 
      return '#' + element; 
     } 
     function parse(forElement){ 
      var element = getElement(forElement); 
      if (element == '#') return; 
      $(forElement) 
       .toggleClass('disabled', !$(element).is(':checked')) 
       .find('input,select,textarea') 
        .each(function(){ 
         if ($(element).is(':checked')) 
          $(this).removeAttr('disabled'); 
         else 
          $(this).attr('disabled','disabled'); 
        }) 
       .end() 
       .find('[class^="for:"]') 
        .each(function() { parse(this); }); 
     } 
     var element = getElement(this); 
     $('[name="' + $(element).attr('name') + '"]') 
      .click(function() { parse(that); }); 
     parse(this); 
    }); 
}); 
+0

Ouch. Suce que même avec jQuery il n'y a pas un moyen plus facile de résoudre cela. Je suis content que vous l'ayez compris! – btelles

+0

ouais, je suis ouvert à toute idée de refactoring :) – Dave

2

Vous êtes probablement à la recherche d'un filtre 'not()'. Sur la ligne que vous avez spécifié, essayez ceci:

.find('input,select,textarea').not("[class='for\\:has-buy-sell-agreementtrue'] *") 

Ou avec les variables que vous avez mentionné:

.find('input,select,textarea').not("[class='for\\:" + element + "true'] *") 

Je ne pense pas que le sélecteur de classe habituelle (le point) fonctionnerait parce que jQuery pourrait penser qu'il est un pseudo-sélecteur comme 'checked' ou 'contains.'

ÉDITÉE

Okie Dokie, je vais lui donner un coup de plus:

Si vous vouliez réduire le nombre d'entrées, sélectionnez et éléments textrea retournés, alors vous pouvez soit réduire la 'trouve' la fonction ou élargit la fonction 'non'. Par exemple, si vous vouliez prendre des mesures que sur des éléments de forme à l'intérieur form_1, alors vous feriez ceci:

.find('#form_1 input, #form_1 select, #form_1 textarea').not("[class='for\\:" + element + "true'] *"); 

est ici un peu plus en détail sur le filtre « non ». Le « non » filtre effectue les opérations suivantes:

  1. trouve tous les éléments du document entier que son sélecteur (le « non » sélecteur) spécifie.
  2. Compare les éléments qu'il trouve à l'étape 1 avec la liste des éléments de la fonction 'find' appelante.
  3. Supprime tous les éléments identiques entre les deux ensembles.
  4. L'ensemble résultant d'éléments inclut uniquement les éléments qui étaient dans la recherche d'origine, mais qui n'étaient pas dans la fonction 'non'.

Par conséquent, l'ensemble des éléments ci-dessus ne comprendrait que des entrées, sélectionnez et les éléments textarea qui ne sont pas dans la classe = pour: a-achat-vente-accord. Oh, et j'ai copié et collé ce filtre sur un simple document html et cela a fonctionné. Donc, vous devriez être tout ensemble!

Plus tard Dave, Bonne chance.

+0

Si cela ne fonctionne pas, faites le moi savoir. – btelles

+0

cela ne fonctionnerait pas car les entrées/select/textarea ne sont pas décorées avec l'attribut "for:". Il y a aussi, un certain codage dur ici, il n'est pas forcément vrai que l'élément va se terminer par "vrai" – Dave

+0

Il y a un astérisque après le sélecteur de classe que vous n'avez pas vu :-) Cela sélectionnerait tous les éléments sous l'élément avec la classe donnée, qui est ce que vous cherchez (je pense). Et le 'vrai' codage dur était un exemple simple de la façon de généraliser le sélecteur de classe basé sur le code précédent. :-) J'espère que cela t'aides! – btelles

2

La fonction filter est agréable pour enlever les articles qui ne pas correspondre à une expression, vous pouvez donc changer cela:

.find('input,select,textarea') 

dans tout cela?

.find('input,select,textarea') 
    .filter(function() { 
     // return false to remove an element from the set 
     return $(this) 
       .closest('[class^="for\\:"]') 
       .hasClass('for:' + element); 
    }) 

L'idée est de supprimer toutes les entrées, ou sélectionne textareas, qui ont un parent le plus proche parent autre que votre choix.

Notez également que j'ai doublé votre caractère : dans votre sélecteur. Le : est un caractère inhabituel à utiliser dans un nom de classe car il implique des sélecteurs "pseudo" et va également trébucher jquery (sauf si vous l'échappez, comme je l'ai ci-dessus).

Bonne chance!


EDIT

Ahh, oui, nous voudrions la désactiver pour propager à travers le imbriquée "pour:" divs, mais pas le permettant .... Nous pourrions modifier cette comme suit en se déplaçant autour du filtrage. (Filtre a été déplacé à une fonction distincte pour le rendre plus lisible.)

.find('input,select,textarea') 
.each(function() { 
    if ($('#'+element).is(':checked')) { 
     if (isDirectDescendant(this, element)) { 
      $(this).removeAttr('disabled'); 
     } 
    } 
    else { 
     $(this).attr('disabled','disabled'); 
    } 
}); 

function isDirectDescendant(e, element) { 
    return $(e) 
      .closest('[class^="for\\:"]') 
      .hasClass('for:' + element); 
} 
+0

En fait, je recommanderais probablement pour la simplicité et la cohérence de simplement remplacer toutes vos instances 'for:' dans 'for-'?Notez également que j'ai changé 'class * =' en 'class^=' de sorte que le nom de classe doit commencer par cette séquence au lieu de simplement le contenir. – Funka

+0

c'est proche, cependant les éléments imbriqués ne sont pas désactivés maintenant. – Dave

+0

J'ai ajouté une mise à jour à ma réponse ... – Funka

0

La restructuration du HTML est la clé:

<li>What would you like to see happen to your business when you (or a co-owner) die, become disabled, or retire? <br/> 
    <ul> 
     <li> 
      <input type="radio" name="loss" id="losssold" value="sold"/> 
      <label for="losssold"> Sold to other Owners/another Business 
      </label> 
      <ul class="nestedList"> 
       <li>Do you have a buy-sell agreement?<br/> 
        <input type="radio" name="has-buy-sell-agreement" id="has-buy-sell-agreementtrue" value="true"/> 
        <label for="has-buy-sell-agreementtrue"> Yes 
        </label> 
        <input type="radio" name="has-buy-sell-agreement" id="has-buy-sell-agreementfalse" value="false"/> 
        <label for="has-buy-sell-agreementfalse"> No 
        </label> 
        <br/> 
        <ol class="nestedList"> 
         <li> 
          <label for="buy-sell-last-updated"> 
           When was the agreement last updated? 
          </label> 
          <br/> 
          <input type="text" name="buy-sell-last-updated" id="buy-sell-last-updated" value=""/> 
         </li> 
         <li>Is the agreement funded? 
          <br/> 
          <input type="radio" name="buy-sell-is-funded" id="buy-sell-is-fundedtrue" value="true"/> 
          <label for="buy-sell-is-fundedtrue"> Yes 
          </label> 
          <input type="radio" name="buy-sell-is-funded" id="buy-sell-is-fundedfalse" value="false"/> 
          <label for="buy-sell-is-fundedfalse"> No 
          </label> 
         </li> 
        </ol> 
       </li> 
      </ul> 
     </li> 
    </ul> 
</li> 

Je déplacé vos <input> balises en dehors des étiquettes de sorte que toutes les entrées avaient un parent direct de <li>. Cela a rendu le jQuery plus facile à trouver. Voici ce que j'ai:

$('.nestedList input, .nestedList select, .nestedList textarea').attr('disabled', 'disabled'); 

$('input:radio').click(function() { 
    $('[name="' + $(this).attr('name') + '"]').each(function() { 
     if($(this).val() != 'false') 
     { 
      if(this.checked == true) 
      { 
       $(this).siblings(".nestedList").children('li').children('input, select, textarea').removeAttr('disabled'); 
      } else 
      { 
       $(this).siblings(".nestedList").children('li').children('input, select, textarea').attr('disabled','disabled'); 
      } 
     } 
    }); 
}); 

J'ai testé cela avec des variations de votre code HTML et semble fonctionner correctement.

Questions connexes