Skip to content

Instantly share code, notes, and snippets.

@cmcnulty
Last active July 26, 2019 07:48
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cmcnulty/7036509 to your computer and use it in GitHub Desktop.
Save cmcnulty/7036509 to your computer and use it in GitHub Desktop.
Allows you to listen for propertychange on setting disabled to true on form elements. Very useful for resolving some select2 issues.
( function( $ ) {
"use strict";
$.fn.fireOnDisable = function( settings ) {
// Only perform this DOM change if we have to watch changes with propertychange
// Also only perform if getOwnPropertyDescriptor exists - IE>=8
// I suppose I could test for "propertychange fires, but not when form element is disabled" - but it would be overkill
if( !( 'onpropertychange' in document.createElement( 'input' ) ) || Object.getOwnPropertyDescriptor === undefined ) {
return this;
}
// IE9-10 use HTMLElement proto, IE8 uses Element proto
var someProto = window.HTMLElement === undefined ? window.Element.prototype : window.HTMLElement.prototype,
someTrigger = function(){},
origDisabled = Object.getOwnPropertyDescriptor( someProto, 'disabled' );
if( document.createEvent ) {
someTrigger = function( newVal ){
var event = document.createEvent('MutationEvent');
/*
Instantiate the event as close to native as possible:
event.initMutationEvent(eventType, canBubble, cancelable, relatedNodeArg, prevValueArg, newValueArg, attrNameArg, attrChangeArg);
*/
event.initMutationEvent( 'DOMAttrModified', true, false, this.getAttributeNode('disabled'), '', '', 'disabled', 1 );
this.dispatchEvent( event );
};
} else if( document.fireEvent ) {
someTrigger = function(){
this.fireEvent( 'onpropertychange' );
};
}
return this.each( function() {
// call prototype's set, and then trigger the change.
Object.defineProperty( this, 'disabled', {
set: function( isDisabled ) {
// We store preDisabled here, so that when we inquire as to the result after throwing the event, it will be accurate
// We can't throw the event after the native send, because it won't be be sent.
// We must do a native fire/dispatch, because native listeners don't catch jquery trigger 'propertychange' events
$.data( this, 'preDisabled', isDisabled );
if ( isDisabled ) {
// Trigger with dispatchEvent
someTrigger.call( this, isDisabled );
}
return origDisabled.set.call( this, isDisabled );
},
get: function() {
var isDisabled = $.data( this, 'preDisabled' );
if( isDisabled === undefined ) {
isDisabled = origDisabled.get.call( this );
}
return isDisabled;
}
});
});
};
})( jQuery );
@meilechh
Copy link

Great work!, however i have one problem, I cannot enable it back with $('#id').prop('disabled', false);

@meilechh
Copy link

Never mind, I removed the if ( isDisabled ), and it worked for me.

@cmcnulty
Copy link
Author

Huh. Not sure what you're seeing @meilechh. the isDisabled test is to prevent the event from being triggered twice - since propertychange should be triggered natively when we're enabling the element. I guess this is why I should have written some unit tests.

@cmcnulty
Copy link
Author

Updated to work with the lastest select2 plugin which uses a native listener, rather than a jQuery listener. This forces me to employ a native trigger, which means that the trigger event has to occur before the disabled property is set - which means that I have to save/retrieve the state using .data rather than using the native state. Which is all to say that it now works.

@cmcnulty
Copy link
Author

Should note that disabling a field with .setAttribute('disabled') does not trigger DOMAttrModified, which means that this won't work in in IE8-10.

@Pilchard123
Copy link

What licence have you released this under? I can't find one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment