开发者

exclude nested children

The following script enables/disables their nested form controls using a for:x class name. The problem is that nested for:y elements are being enabled when they shouldn't be. They should only be enabled when the nested for:y is enabled.

Can someone provide me with an additional filter to exclude the form elements that are inside a nested for:* class?

In other words, given the example below, when losssold is checked then all the child inputs of for:losssold should be enabled except for the child element elements inside for:has-buy-sell-agreementtrue if has-buy-sell-agreementtrue is not also checked.

So I thought it would be easier to just figure to exclude those elements in 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 disa开发者_JS百科bled, 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>


The filter function is nice for removing items that do not match an expression, so you could change this:

.find('input,select,textarea') 

into this?

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

The idea is to remove any inputs, selects, or textareas, which have a closest parent other than your desired parent.

Notice also that I double-escaped your : character in your selector. The : is an unusual character to use in a class name because it implies "pseudo" selectors and will also trip up jquery (unless you escape it, as I have above).

Good luck!


EDIT

Ahh, yes, we would want the disabling to propogate down through the nested "for:" divs, but not the enabling.... We could modify this as follows by moving around the filtering. (Filter was moved to a separate function to make it more readable.)

.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);
}


You're probably looking for a 'not()' filter. At the line you specified, try this:

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

Or with the variables you mentioned:

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

I don't think the usual class selector (the dot) would work because jQuery might think it's a pseudo selector like 'checked' or 'contains.'

EDITED

Okie dokie, I'll give it one more shot:

IF you wanted to narrow the number of input, select, and textrea elements returned, then you could either narrow the 'find' function or broaden the 'not' function. For example, If you wanted to take action only on form elements inside form_1, then you'd do this:

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

Here's a little more detail on the 'not' filter. The 'not' filter does the following:

  1. Finds all elements in the entire document that its selector (the 'not' selector) specifies.
  2. Compares the elements that it finds in step 1 with the list of elements in the calling 'find' function.
  3. Removes all elements that are the same between the two sets.
  4. The resulting set of elements includes only those elements that were in the original find, but were not in the 'not' function.

Therefore, the set of elements above would only include input, select, and textarea elements that were not inside the class=for:has-buy-sell-agreement.

Oh, and I copied and pasted this filter onto a simple html document and it worked. So you should be all set!

Later Dave, Best of luck.


Restructuring the HTML is the key:

<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>

I moved your <input> tags outside of the labels so that all inputs had a direct parent of <li>. This made the jQuery easier to come up with. Here's what I have:

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

I've tested this with variations of your HTML and seems to work fine.


I ended up solving this by recursively parsing the child for^= elements when the event was fired.

$(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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜