Created
March 27, 2014 11:32
-
-
Save Skateside/9805540 to your computer and use it in GitHub Desktop.
Nothing short of a hack, but a surprisingly useful one when you can't change the main scripts.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Add a handler to check if a class changes on a given element. | |
* The handler will get two arguments: the new class and the old class. | |
* | |
* @param {Element} element Element whos class changes should be watched. | |
* @param {Function} func Function to call when the class changes. | |
*/ | |
var onclasschange = (function () { | |
'use strict'; | |
var MutationObserver = null, | |
classWatcher = null, | |
pollTime = 125; | |
/** | |
* Checks to see if the DOMAttrModified MutationEvent works correctly (it | |
* does not in Webkit) | |
* | |
* http://engineering.silk.co/post/31921750832/mutation-events-what-happens? | |
* https://bugs.webkit.org/show_bug.cgi?id=8191 | |
* | |
* @return {boolean} true if the DOMAttrModified MutationEvent is working | |
* correctly, false otherwise. | |
*/ | |
function isMutationEventsWorking() { | |
var attrModifiedWorks = false, | |
listener = function () { | |
attrModifiedWorks = true; | |
}, | |
documentElement = document.documentElement; | |
documentElement.addEventListener("DOMAttrModified", listener, false); | |
documentElement.setAttribute("___TEST___", true); | |
documentElement.removeAttribute("___TEST___", true); | |
documentElement.removeEventListener("DOMAttrModified", listener, false); | |
return attrModifiedWorks; | |
} | |
/** | |
* A simple check to see if MutationObservers exist (even if it's in a | |
* prefixed state). | |
* | |
* @type {?Function} A MutationObserver function if found or null if the | |
* browser does not support MutationObservers. | |
*/ | |
MutationObserver = window.MutationObserver || (function () { | |
var prefixes = ['Ms', 'O', 'Moz', 'Webkit'], | |
i = 0, | |
il = prefixes.length, | |
Obs = null, | |
temp = null; | |
while (i < il) { | |
temp = window[prefixes[i] + 'MutationObserver']; | |
if (typeof temp === 'function') { | |
Obs = temp; | |
break; | |
} | |
i += 1; | |
} | |
return Obs; | |
}()); | |
/** | |
* If the browser supports MutationObservers, we can use the most efficient | |
* way of tracking a class change. | |
*/ | |
if (MutationObserver) { | |
classWatcher = function (element, func) { | |
var observer = new MutationObserver(function (mutations) { | |
var i = 0, | |
il = mutations.length, | |
m = null; | |
while (i < il) { | |
m = mutations[i]; | |
if (m.type === 'attributes' && | |
m.attributeName === 'class') { | |
/** | |
* In some browsers, oldValue can be null before the | |
* element has a className. We swap that to an empty | |
* string to make the function parameters easier to | |
* manage (and it's still accurate). | |
*/ | |
func.call(element, element.className, m.oldValue || ''); | |
break; | |
} | |
i += 1; | |
} | |
}); | |
observer.observe(element, { | |
attributes: true, | |
childList: true, | |
characterData: true, | |
attributeOldValue: true | |
}); | |
}; | |
/** | |
* If the browser does not support the MutationObserver but does support the | |
* MutationEvent, we should use that. We should be careful to check that | |
* DOMAttrModified works correctly since that is the event we will bind to. | |
*/ | |
} else if (('MutationEvent' in window) && | |
typeof document.addEventListener === 'function' && | |
isMutationEventsWorking()) { | |
classWatcher = function (element, func) { | |
element.addEventListener('DOMAttrModified', function (e) { | |
if (e.attrName === 'class') { | |
func.call(element, e.newValue, e.prevValue); | |
} | |
}, false); | |
}; | |
/** | |
* If MutationObserver and MutationEvent support doesn't exist in the | |
* browser, we make a quick check for MSIE's onpropertychange event. Binding | |
* to that event will be more efficient than the polling. | |
*/ | |
} else if ('onpropertychange' in document.createElement('_') && | |
typeof document.attachEvent !== 'undefined') { | |
classWatcher = function (element, func) { | |
var oldValue = element.className; | |
element.attachEvent('onpropertychange', function () { | |
if (window.event.propertyName === 'className') { | |
func.call(element, element.className, oldValue); | |
oldValue = element.className; | |
} | |
}); | |
}; | |
/** | |
* At this stage, MutationObserver, MutationEvent and onpropertychange are | |
* not supported by the browser. Our only alternative is to repeatedly check | |
* an element to see if a value has changed and execute the function if it | |
* has. | |
*/ | |
} else { | |
classWatcher = function (element, func) { | |
var oldValue = element.className, | |
poll = function () { | |
var className = element.className; | |
if (className !== oldValue) { | |
func.call(element, className, oldValue); | |
oldValue = className; | |
} | |
window.setTimeout(poll, pollTime); | |
}; | |
poll(); | |
}; | |
} | |
return classWatcher; | |
}()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Just thinking aloud here ... could pass the
newClass
andoldClass
arguments through something like this, to make it easier to check if a certain class was contained: