-1

我有以下由一些 UI 开发人员开发的 ui-spinner.js 文件:

(function($, undefined) {

var 
// constants
active = 'ui-state-active',
hover = 'ui-state-hover',
disabled = 'ui-state-disabled',

keyCode = $.ui.keyCode,
up = keyCode.UP,
down = keyCode.DOWN,
right = keyCode.RIGHT,
left = keyCode.LEFT,
pageUp = keyCode.PAGE_UP,
pageDown = keyCode.PAGE_DOWN,
home = keyCode.HOME,
end = keyCode.END,

msie = $.browser.msie,
mouseWheelEventName = $.browser.mozilla ? 'DOMMouseScroll' : 'mousewheel',

// namespace for events on input
eventNamespace = '.uispinner', 

// only these special keys will be accepted, all others will be ignored unless CTRL or ALT are pressed
validKeys = [up, down, right, left, pageUp, pageDown, home, end, keyCode.BACKSPACE, keyCode.DELETE, keyCode.TAB],

// stores the currently focused spinner
// Note: due to oddities in the focus/blur events, this is part of a two-part system for confirming focus
// this must set to the control, and the focus variable must be true
// this is because hitting up/down arrows with mouse causes focus to change, but blur event for previous control doesn't fire
focusCtrl;

$.widget('ui.spinner', {
options: {
    min: null,
    max: null,
    allowNull: false,

    group: '',
    point: '.',
    prefix: '',
    suffix: '',
    places: null, // null causes it to detect the number of places in step

    defaultStep: 1, // real value is 'step', and should be passed as such.  This value is used to detect if passed value should override HTML5 attribute
    largeStep: 10,
    mouseWheel: true,
    increment: 'slow',      
    className: null,
    showOn: 'always',
    width: 16,
    upIconClass: "ui-icon-triangle-1-n",
    downIconClass: "ui-icon-triangle-1-s",

    format: function(num, places) {
        var options = this,
            regex = /(\d+)(\d{3})/,
            result = ((isNaN(num) ? 0 : Math.abs(num)).toFixed(places)) + '';

        for (result = result.replace('.', options.point); regex.test(result) && options.group; result=result.replace(regex, '$1'+options.group+'$2')) {};
        return (num < 0 ? '-' : '') + options.prefix + result + options.suffix;
    },

    parse: function(val) {
        var options = this;

        if (options.group == '.')
            val = val.replace('.', '');
        if (options.point != '.')
            val = val.replace(options.point, '.');
        return parseFloat(val.replace(/[^0-9\-\.]/g, ''));
    }
},

// * Widget fields *
// curvalue - current value
// places - currently effective number of decimal places
// oWidth - original input width (used for destroy)
// oMargin - original input right margin (used for destroy)
// counter - number of spins at the current spin speed
// incCounter - index within options.increment of the current spin speed
// selfChange - indicates that change event is being fired by the widget, so don't reprocess input value
// inputMaxLength - initial maxLength value on the input
// focused - this spinner currently has the focus

_create: function() {
    // shortcuts
    var self = this,
        input = self.element,
        type = input.attr('type');

    if (!input.is('input') || ((type != 'text') && (type != 'number'))) {
        console.error('Invalid target for ui.spinner');
        return;
    }

    self._procOptions(true);
    self._createButtons(input);

    if (!input.is(':enabled'))
        self.disable();
},

_createButtons: function(input) {
    function getMargin(margin) {
        // IE8 returns auto if no margin specified
        return margin == 'auto' ? 0 : parseInt(margin);
    }

    var self = this,
        options = self.options,
        className = options.className,
        buttonWidth = options.width,
        showOn = options.showOn,
        box = $.support.boxModel,
        height = input.outerHeight(),
        rightMargin = self.oMargin = getMargin(input.css('margin-right')), // store original width and right margin for later destroy
        wrapper = self.wrapper = input.css({ width: (self.oWidth = (box ? input.width() : input.outerWidth())) - buttonWidth, 
                                             marginRight: rightMargin + buttonWidth, textAlign: 'right' })
            .after('<span class="ui-spinner ui-widget"></span>').next(),
        btnContainer = self.btnContainer = $(
            '<div class="ui-spinner-buttons">' + 
                '<div class="ui-spinner-up ui-spinner-button ui-state-default ui-corner-tr"><span class="ui-icon '+options.upIconClass+'">&nbsp;</span></div>' + 
                '<div class="ui-spinner-down ui-spinner-button ui-state-default ui-corner-br"><span class="ui-icon '+options.downIconClass+'">&nbsp;</span></div>' + 
            '</div>'),

        // object shortcuts
        upButton, downButton, buttons, icons,

        hoverDelay,
        hoverDelayCallback,

        // current state booleans
        hovered, inKeyDown, inSpecialKey, inMouseDown,

        // used to reverse left/right key directions
        rtl = input[0].dir == 'rtl';

    // apply className before doing any calculations because it could affect them
    if (className) wrapper.addClass(className);

    wrapper.append(btnContainer.css({ height: height, left: -buttonWidth-rightMargin,
        // use offset calculation to fix vertical position in Firefox
        top: (input.offset().top - wrapper.offset().top) + 'px' }));

    buttons = self.buttons = btnContainer.find('.ui-spinner-button');
    buttons.css({ width: buttonWidth - (box ? buttons.outerWidth() - buttons.width() : 0), height: height/2 - (box ? buttons.outerHeight() - buttons.height() : 0) });
    upButton = buttons[0];
    downButton = buttons[1];

    // fix icon centering
    icons = buttons.find('.ui-icon');
    icons.css({ marginLeft: (buttons.innerWidth() - icons.width()) / 2, marginTop:  (buttons.innerHeight() - icons.height()) / 2 });

    // set width of btnContainer to be the same as the buttons
    btnContainer.width(buttons.outerWidth());
    if (showOn != 'always')
        btnContainer.css('opacity', 0);

    /* Event Bindings */

    // bind hover events to show/hide buttons
    if (showOn == 'hover' || showOn == 'both')
        buttons.add(input)
            .bind('mouseenter' + eventNamespace, function() {
                setHoverDelay(function() {
                    hovered = true;
                    if (!self.focused || (showOn == 'hover')) // ignore focus flag if show on hover only
                        self.showButtons();
                });
            })

            .bind('mouseleave' + eventNamespace, function hoverOut() {
                setHoverDelay(function() {
                    hovered = false;
                    if (!self.focused || (showOn == 'hover')) // ignore focus flag if show on hover only
                        self.hideButtons();
                });
            });


    buttons.hover(function() {
                // ensure that both buttons have hover removed, sometimes they get left on
                self.buttons.removeClass(hover);

                if (!options.disabled)
                    $(this).addClass(hover);
            }, function() {
                $(this).removeClass(hover);
            })
        .mousedown(mouseDown)
        .mouseup(mouseUp)
        .mouseout(mouseUp);

    if (msie)
        // fixes dbl click not firing second mouse down in IE
        buttons.dblclick(function() {
                if (!options.disabled) {
                    // make sure any changes are posted
                    self._change();
                    self._doSpin((this === upButton ? 1 : -1) * options.step);
                }

                return false;
            }) 

            // fixes IE8 dbl click selection highlight
            .bind('selectstart', function() {return false;});

    input.bind('keydown' + eventNamespace, function(e) {
                var dir, large, limit,
                    keyCode = e.keyCode; // shortcut for minimization
                if (e.ctrl || e.alt) return true; // ignore these events

                if (isSpecialKey(keyCode))
                    inSpecialKey = true;

                if (inKeyDown) return false; // only one direction at a time, and suppress invalid keys

                switch (keyCode) {
                    case up:
                    case pageUp:
                        dir = 1;
                        large = keyCode == pageUp;
                        break;

                    case down:
                    case pageDown:
                        dir = -1;
                        large = keyCode == pageDown;
                        break;

                    case right:
                    case left:
                        dir = (keyCode == right) ^ rtl ? 1 : -1;
                        break;

                    case home:
                        limit = self.options.min;
                        if (limit != null) self._setValue(limit);
                        return false;

                    case end:
                        limit = self.options.max;
                        limit = self.options.max;
                        if (limit != null) self._setValue(limit);
                        return false;
                }

                if (dir) { // only process if dir was set above
                    if (!inKeyDown && !options.disabled) {
                        keyDir = dir;

                        $(dir > 0 ? upButton : downButton).addClass(active);
                        inKeyDown = true;
                        self._startSpin(dir, large);
                    }

                    return false;
                }
            })

        .bind('keyup' + eventNamespace, function(e) {
                if (e.ctrl || e.alt) return true; // ignore these events

                if (isSpecialKey(keyCode))
                    inSpecialKey = false;

                switch (e.keyCode) {
                    case up:
                    case right:
                    case pageUp:
                    case down:
                    case left:
                    case pageDown:
                        buttons.removeClass(active)
                        self._stopSpin();
                        inKeyDown = false;
                        return false;
                }
            })

        .bind('keypress' + eventNamespace, function(e) {
                if (invalidKey(e.keyCode, e.charCode)) return false;
            })

        .bind('change' + eventNamespace, function() { self._change(); })

        .bind('focus' + eventNamespace, function() {
                function selectAll() {
                    self.element.select();
                }

                msie ? selectAll() : setTimeout(selectAll, 0); // add delay for Chrome, but breaks IE8
                self.focused = true;
                focusCtrl = self;
                if (!hovered && (showOn == 'focus' || showOn == 'both')) // hovered will only be set if hover affects show
                    self.showButtons();
            })

        .bind('blur' + eventNamespace, function() {
                self.focused = false;
                if (!hovered && (showOn == 'focus' || showOn == 'both')) // hovered will only be set if hover affects show
                    self.hideButtons();
            });

    function isSpecialKey(keyCode) {
        for (var i=0; i<validKeys.length; i++) // predefined list of special keys
            if (validKeys[i] == keyCode) return true;

        return false;
    }

    function invalidKey(keyCode, charCode) {
        if (inSpecialKey) return false;             

        var ch = String.fromCharCode(charCode || keyCode),
            options = self.options;

        if ((ch >= '0') && (ch <= '9') || (ch == '-')) return false;
        if (((self.places > 0) && (ch == options.point))
            || (ch == options.group)) return false;

        return true;
    }

    // used to delay start of hover show/hide by 100 milliseconds
    function setHoverDelay(callback) {
        if (hoverDelay) {
            // don't do anything if trying to set the same callback again
            if (callback === hoverDelayCallback) return;

            clearTimeout(hoverDelay);
        }

        hoverDelayCallback = callback;
        hoverDelay = setTimeout(execute, 100);

        function execute() {
            hoverDelay = 0;
            callback();
        }
    }

    function mouseDown() {
        if (!options.disabled) {
            var input = self.element[0],
                dir = (this === upButton ? 1 : -1);

            input.focus();
            input.select();
            $(this).addClass(active);

            inMouseDown = true;
            self._startSpin(dir);
        }

        return false;
    }

    function mouseUp() {
        if (inMouseDown) {
            $(this).removeClass(active);
            self._stopSpin();
            inMouseDown = false;
        }
        return false;
    }
},

_procOptions: function(init) {
    var self = this,
        input = self.element,
        options = self.options,
        min = options.min,
        max = options.max,
        step = options.step,
        places = options.places,
        maxlength = -1, temp;

    // setup increment based on speed string
    if (options.increment == 'slow')
        options.increment = [{count: 1, mult: 1, delay: 250},
                             {count: 3, mult: 1, delay: 100},
                             {count: 0, mult: 1, delay: 50}];
    else if (options.increment == 'fast')
        options.increment = [{count: 1, mult: 1, delay: 250},
                             {count: 19, mult: 1, delay: 100},
                             {count: 80, mult: 1, delay: 20},
                             {count: 100, mult: 10, delay: 20},
                             {count: 0, mult: 100, delay: 20}];

    if ((min == null) && ((temp = input.attr('min')) != null))
        min = parseFloat(temp);

    if ((max == null) && ((temp = input.attr('max')) != null))
        max = parseFloat(temp);

    if (!step && ((temp = input.attr('step')) != null))
        if (temp != 'any') {
            step = parseFloat(temp);
            options.largeStep *= step;
        }
    options.step = step = step || options.defaultStep;

    // Process step for decimal places if none are specified
    if ((places == null) && ((temp = step + '').indexOf('.') != -1))
        places = temp.length - temp.indexOf('.') - 1;
    self.places = places;

    if ((max != null) && (min != null)) {
        // ensure that min is less than or equal to max
        if (min > max) min = max;

        // set maxlength based on min/max
        maxlength = Math.max(Math.max(maxlength, options.format(max, places, input).length), options.format(min, places, input).length);
    }

    // only lookup input maxLength on init
    if (init) self.inputMaxLength = input[0].maxLength;
    temp = self.inputMaxLength;

    if (temp > 0) {
        maxlength = maxlength > 0 ? Math.min(temp, maxlength) : temp;
        temp = Math.pow(10, maxlength) - 1;
        if ((max == null) || (max > temp))
            max = temp;
        temp = -(temp + 1) / 10 + 1;
        if ((min == null) || (min < temp))
            min = temp;
    }

    if (maxlength > 0)
        input.attr('maxlength', maxlength);

    options.min = min;
    options.max = max;

    // ensures that current value meets constraints
    self._change();

    input.unbind(mouseWheelEventName + eventNamespace);
    if (options.mouseWheel)
        input.bind(mouseWheelEventName + eventNamespace, self._mouseWheel);
},

_mouseWheel: function(e) {
    var self = $.data(this, 'spinner');
    if (!self.options.disabled && self.focused && (focusCtrl === self)) {
        // make sure changes are posted
        self._change();
        self._doSpin(((e.wheelDelta || -e.detail) > 0 ? 1 : -1) * self.options.step);
        return false;
    }
},

// sets an interval to call the _spin function
_setTimer: function(delay, dir, large) {
    var self = this;
    self._stopSpin();
    self.timer = setInterval(fire, delay);

    function fire() {
        self._spin(dir, large);
    }
},

// stops the spin timer
_stopSpin: function() {
    if (this.timer) {
        clearInterval(this.timer);
        this.timer = 0;
    }
},

// performs first step, and starts the spin timer if increment is set
_startSpin: function(dir, large) {
    // shortcuts
    var self = this,
        options = self.options,
        increment = options.increment;

    // make sure any changes are posted
    self._change();
    self._doSpin(dir * (large ? self.options.largeStep : self.options.step));

    if (increment && increment.length > 0) {        
        self.counter = 0;
        self.incCounter = 0;
        self._setTimer(increment[0].delay, dir, large);
    }
},

// called by timer for each step in the spin
_spin: function(dir, large) {
    // shortcuts
    var self = this,
        increment = self.options.increment,
        curIncrement = increment[self.incCounter];

    self._doSpin(dir * curIncrement.mult * (large ? self.options.largeStep : self.options.step));
    self.counter++;

    if ((self.counter > curIncrement.count) && (self.incCounter < increment.length-1)) {
        self.counter = 0;
        curIncrement = increment[++self.incCounter];
        self._setTimer(curIncrement.delay, dir, large);
    }
},

// actually spins the timer by a step
_doSpin: function(step) {
    // shortcut
    var self = this,
        value = self.curvalue;

    if (value == null)
        value = (step > 0 ? self.options.min : self.options.max) || 0;

    self._setValue(value + step);
},

// Parse the value currently in the field
_parseValue: function() {
    var value = this.element.val();
    return value ? this.options.parse(value, this.element) : null;
},

_validate: function(value) {
    var options = this.options,
        min = options.min,
        max = options.max;

    if ((value == null) && !options.allowNull)
        value = this.curvalue != null ? this.curvalue : min || max || 0; // must confirm not null in case just initializing and had blank value

    if ((max != null) && (value > max))
        return max;
    else if ((min != null) && (value < min))
        return min;
    else
        return value;
},

_change: function() {
    var self = this, // shortcut
        value = self._parseValue(),
        min = self.options.min,
        max = self.options.max;

    // don't reprocess if change was self triggered
    if (!self.selfChange) {
        if (isNaN(value))
            value = self.curvalue;

        self._setValue(value, true);
    }
},

// overrides _setData to force option parsing
_setOption: function(key, value) {
    $.Widget.prototype._setOption.call(this, key, value);
    this._procOptions();
},

increment: function() {
    this._doSpin(this.options.step);
},

decrement: function() {
    this._doSpin(-this.options.step);
},

showButtons: function(immediate) {
    var btnContainer = this.btnContainer.stop();
    if (immediate)
        btnContainer.css('opacity', 1);
    else
        btnContainer.fadeTo('fast', 1);
},

hideButtons: function(immediate) {
    var btnContainer = this.btnContainer.stop();
    if (immediate)
        btnContainer.css('opacity', 0);
    else
        btnContainer.fadeTo('fast', 0);
    this.buttons.removeClass(hover);
},

// Set the value directly
_setValue: function(value, suppressFireEvent) {
    var self = this;

    self.curvalue = value = self._validate(value);
    self.element.val(value != null ? 
        self.options.format(value, self.places, self.element) :
        '');

    if (!suppressFireEvent) {
        self.selfChange = true;
        self.element.change();
        self.selfChange = false;
    }
},

// Set or retrieve the value
value: function(newValue) {
    if (arguments.length) {
        this._setValue(newValue);

        // maintains chaining
        return this.element;
    }

    return this.curvalue;
},

enable: function() {
    this.buttons.removeClass(disabled);
    this.element[0].disabled = false;
    $.Widget.prototype.enable.call(this);
},

disable: function() {
    this.buttons.addClass(disabled)
        // in case hover class got left on
        .removeClass(hover);

    this.element[0].disabled = true;
    $.Widget.prototype.disable.call(this);
},

destroy: function(target) {
    this.wrapper.remove();
    this.element.unbind(eventNamespace).css({ width: this.oWidth, marginRight: this.oMargin });

    $.Widget.prototype.destroy.call(this);
}   
});

 })( jQuery );

我有一组 3 个微调器,写成:

  <input type="text" id="rate"name="rate" class="spinner" value="0" />

单击单选按钮时,我希望禁用此输入框以及微调器。如果我将 disabled="disabled" 设置为,则只有输入框被禁用,但微调器向上按钮和向下按钮有效。我希望它也被禁用。

其次,我希望这发生在我的 1 个特定输入框上,如果我更改了所有我不想要的微调器上发生的微调器的某些属性。

4

1 回答 1

1

我已经使用了来自 GitHub 的 jQuery.ui.spinner 1.20,并且禁用选项按您的预期工作。

例如在您的代码中,您可以执行以下操作:

$('#rate').spinner("disable");  // 'rate' is the HTML input id

请确保每个输入的 id 不同。

这将导致禁用输入以及禁用微调器的按钮。

请查看https://github.com/btburnett3/jquery.ui.spinner以下载此扩展的最新版本。

于 2012-09-17T03:00:44.410 回答