Skip to main content

Bug Tracker

Side navigation

#1209 closed enhancement (wontfix)

Opened May 18, 2007 07:27PM UTC

Closed May 11, 2008 11:38PM UTC

Last modified May 13, 2008 05:31PM UTC

Form Value Function

Reported by: bgoldman Owned by:
Priority: minor Milestone: 1.2.4
Component: core Version: 1.1.2
Keywords: Cc:
Blocked by: Blocking:
Description

This function sets or gets the value of a form variable as a form would interpret it. This function is useful for jQuery core because everybody uses forms. This function makes it very easy to get and set form values without having to use fancy selectors. It's basically shorthand for a bunch of alternatives.

1) For selects and options: formVal() would return the currently selected value. formVal('foo') would select the option with that value.

2) For input text fields: formVal() would return the current value. formVal('foo') would set the value to foo.

3) For textareas: formVal() would return text(). formVal('foo') would set the text() to foo.

4) For checkboxes: formVal() would return the first matched checked value. formVal('foo') would check the boxes with value foo.

5) For radios: formVal() would return the checked value. formVal('foo') would check the radio with value foo.

    //gets the value of the first matched form field, or sets the value of all the matched form fields
    $.fn.formVal = function(newVal) {
        var self = this.get(0);
    
        if(newVal == null) {
            if(this.size() < 1) return '';
            else if(self.type == 'text') return self.value;
            else if(self.tagName == 'TEXTAREA') return this.text();
            else if(self.type == 'checkbox' || self.type == 'radio') return this.filter('input:checked').val();
            else if(self.tagName == 'OPTION') return this.parent().formVal();
            else if(self.tagName == 'SELECT') {
                var match = this.find('option:selected').eq(0);
                return (match.get(0).value != null) ? match.val() : match.text();
            }
            else return '';
        }
        
        if(self.type == 'text') this.val(newVal);
        else if(self.tagName == 'TEXTAREA') this.text(newVal);
        else if(self.type == 'checkbox' || self.type == 'radio') {
            var matches =  this.filter('[@value=' + newVal + ']');
            if(matches.size() < 1) return this;
            if(self.type == 'radio') this.filter(':checked').removeAttr('checked');
            matches.attr('checked', 'checked');
        }
        else if(self.tagName == 'OPTION') this.parent().formVal(newVal);
        else if(self.tagName == 'SELECT') {
            var match = this.find('option[@value=' + newVal + ']');

            if(match.size() < 1) {
                if(newVal == '') match = this.find('option:empty');
                else match = this.find('option').contains(newVal);
            }

            if(match.size() < 1) return this;
            this.find('option:selected').removeAttr('selected');
            match.eq(0).attr('selected', 'selected');
        }
        
        return this;
    };
Attachments (0)
Change History (6)

Changed May 18, 2007 08:04PM UTC by bgoldman comment:1

I found a bug with selects and options. Here is the fixed code:

    //gets the value of the first matched form field, or sets the value of all the matched form fields
    $.fn.formVal = function(newVal) {
        var self = this.get(0);
    
        if(newVal == null) {
            if(this.size() < 1) return '';
            else if(self.type == 'text') return self.value;
            else if(self.tagName == 'TEXTAREA') return this.text();
            else if(self.type == 'checkbox' || self.type == 'radio') return this.filter('input:checked').val();
            else if(self.tagName == 'OPTION') return this.parent().formVal();
            else if(self.tagName == 'SELECT') {
                var match = this.find('option:selected').eq(0);
                return (match.is('[@value]')) ? match.val() : match.text();
            }
            else return '';
        }
        
        if(self.type == 'text') this.val(newVal);
        else if(self.tagName == 'TEXTAREA') this.text(newVal);
        else if(self.type == 'checkbox' || self.type == 'radio') {
            var matches =  this.filter('[@value=' + newVal + ']');
            if(matches.size() < 1) return this;
            if(self.type == 'radio') this.filter(':checked').removeAttr('checked');
            matches.attr('checked', 'checked');
        }
        else if(self.tagName == 'OPTION') this.parent().formVal(newVal);
        else if(self.tagName == 'SELECT') {
            var match = this.filter('option[@value=' + newVal + ']');

            if(match.size() < 1) {
                if(newVal == '') match = this.find('option:empty');
                else match = this.find('option').contains(newVal);
            }

            if(match.size() < 1) return this;
            this.find('option:selected').removeAttr('selected');
            match.eq(0).attr('selected', 'selected');
        }
        
        return this;
    };

Changed May 19, 2007 12:41AM UTC by bgoldman comment:2

Fixed another bug... here we go:

    //gets the value of the first matched form field, or sets the value of all the matched form fields
    $.fn.formVal = function(newVal) {
        var self = this.get(0);
    
        if(newVal == null) {
            if(this.size() < 1) return '';
            else if(self.type == 'text') return self.value;
            else if(self.tagName == 'TEXTAREA') return this.text();
            else if(self.type == 'checkbox' || self.type == 'radio') return this.filter('input:checked').val() || '';
            else if(self.tagName == 'OPTION') return this.parent().formVal();
            else if(self.tagName == 'SELECT') {
                var match = this.find('option:selected').eq(0);
                return (match.is('[@value]')) ? match.val() : match.text();
            }
            else return '';
        }

        if(self.type == 'text') this.val(newVal);
        else if(self.tagName == 'TEXTAREA') this.text(newVal);
        else if(self.type == 'checkbox' || self.type == 'radio') {
            this.filter(':checked').removeAttr('checked');
            this.filter('[@value=' + newVal + ']').attr('checked', 'checked');
        }
        else if(self.tagName == 'OPTION') this.parent().formVal(newVal);
        else if(self.tagName == 'SELECT') {
            var match = this.filter('option[@value=' + newVal + ']');

            if(match.size() < 1) {
                if(newVal == '') match = this.find('option:empty');
                else match = this.find('option').filter(function() {
                    return ($(this).text() == newVal);
                });
            }

            if(match.size() < 1) return this;
            this.find('option:selected').removeAttr('selected');
            match.attr('selected', 'selected');
        }
        
        return this;
    };

Changed June 20, 2007 09:08PM UTC by mrclay comment:3

For SELECT elements, formVal() returns the wrong value when the selected OPTION is marked up with value="": Since "" is falsy, match.is('[@value]') is false and match.text() is incorrectly returned.

Pseudocode for a fix:

if (option.value) { // non-empty string
    return option.value;
}
if (! option.outerHTML) { // definitely not IE, so trust value
    return '';
}
if (/* option.outerHTML has 'value=""' in attributes */) { // likely painful regex
    return '';
} else {
    return option.text;
}

Changed June 21, 2007 07:25PM UTC by mrclay comment:4

Here's a fix for setting/getting SELECT when options might have empty value attributes, but non-empty text. Is there a unit test somewhere for this plugin?

//gets the value of the first matched form field, or sets
//the value of all the matched form fields
$.fn.formVal = function(newVal) {
    var self = this.get(0);
    var optVal = function(opt) {
        if(opt.value) return opt.value; // non-empty string, use it
        if(!opt.outerHTML) return ''; // not IE, we should trust value
        // @todo: more rigorous regex to assert value="" in attributes, not text
        return /\\svalue=""(?:\\s|>)/.test(opt.outerHTML) ? '' : opt.text;
    };
    if(newVal == null) {
        if(this.size() < 1) return '';
        if(self.type == 'text') return self.value;
        if(self.tagName == 'TEXTAREA') return this.text();
        if(self.type == 'checkbox' || self.type == 'radio')
          return this.filter('input:checked').val() || '';
        if(self.tagName == 'OPTION') return this.parent().formVal();
        // if a SELECT with no selection, fallthrough to return ''
        // or should re return null?
        if(self.tagName == 'SELECT' && self.selectedIndex >= 0)
          return optVal(self.options[self.selectedIndex]);
        return '';
    }
    if(self.type == 'text') this.val(newVal);
    else if(self.tagName == 'TEXTAREA') this.text(newVal);
    else if(self.type == 'checkbox' || self.type == 'radio') {
        this.filter(':checked').removeAttr('checked');
        this.filter('[@value=' + newVal + ']').attr('checked', 'checked');
    }
    else if(self.tagName == 'OPTION') this.parent().formVal(newVal);
    else if(self.tagName == 'SELECT') {
        // search for matching value
        for (var i=0, l=self.options.length; i<l; ++i) {
            if(newVal == optVal(self.options[i])) {
                self.selectedIndex = i;
                break;
            }
        }
    }
    return this;
};

Changed May 11, 2008 11:38PM UTC by flesler comment:5

resolution: → wontfix
status: newclosed

I think jQuery.form can do all or most of this. If anything is missing you can propose it for this plugin.

Changed May 13, 2008 05:31PM UTC by flesler comment:6

milestone: 1.1.31.2.4