Skip to content

Instantly share code, notes, and snippets.

@pherris
Last active August 29, 2015 14:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pherris/5f49737dfb06716634f8 to your computer and use it in GitHub Desktop.
Save pherris/5f49737dfb06716634f8 to your computer and use it in GitHub Desktop.
Ext Element Cache with MutationSummary Library
<!DOCTYPE html>
<html>
<head>
<title>ExtJS Cache Example</title>
<link href="http://cdn.sencha.com/ext/gpl/4.2.0/resources/css/ext-all.css" rel="stylesheet" />
<script src="http://cdn.sencha.com/ext/gpl/4.2.0/ext-all-debug.js"></script>
<body>
</body>
<script>
/**
* this file shows an example of caching the width and height attributes of DOM elements
* in ExtJS when calling the getters and clearing the cache with the setters AND with the
* MutationObserver (NOT MUTATIONSUMMARY) functionality to detect attribute changes.
*
* This implementation also has the potential to be a little race-condition-y but
* demonstrates the concept and gaps well enough.
*
*/
Ext.application({
name : 'Fiddle',
launch : function() {
Ext.define('Rally.ui.overrides.Element', {
override: 'Ext.dom.Element',
cachedHeight: null,
cachedWidth: null,
_clearCache: function () {
console.log('clear cache');
this.cachedHeight = null;
this.cachedWidth = null;
},
setWidth: function (width) {
this._clearCache();
this.callParent([width]);
},
setHeight: function (height) {
this._clearCache();
this.callParent([height]);
},
getWidth: function () {
if (this.cachedWidth) {
console.log('cached w');
} else {
this.cachedWidth = this.callParent([]);
console.log('non cached w');
}
return this.cachedWidth;
},
getHeight: function () {
if (this.cachedHeight) {
console.log('cached h');
} else {
this.cachedHeight = this.callParent([]);
console.log('non cached h');
}
return this.cachedHeight;
}
});
// create an observer instance
var mObserver = new MutationObserver(function(mutations) {
var cacheInvalidatedList = {};
mutations.forEach(function(mutation) {
if (!mutation.target || !mutation.target.id || cacheInvalidatedList[mutation.target.id] === true) {
return;
}
var extDomElement = Ext.get(mutation.target.id);
if (extDomElement._clearCache) {
extDomElement._clearCache();
cacheInvalidatedList[mutation.target.id] = true;
}
});
});
var config = { attributes: true, childList: true, characterData: true, attributeFilter: ['style'] };
var testDiv = Ext.DomHelper.append(Ext.getBody(), '<div id="testDiv" style="border-radius: 10px">observe me!</div>', true);
mObserver.observe(testDiv.dom, config);
testDiv.setStyle('border', '3px solid #BADA55');
//sets the cache
console.log('Default width: ' + testDiv.getWidth());
//change the native DOM style - Ext knows nothing about this, but the mutation observer does
testDiv.dom.style.width = 450 + 'px';
console.log('I still have the old value even though the DOM is at 450: ' + testDiv.getWidth());
testDiv.setWidth(450);
console.log('I now have the right value (450) since we used a setter to set the size: ' + testDiv.getWidth());
//let the MutationSummary library detect the DOM change and invalidate our cache
setTimeout(function () {
var radius = Math.floor(Math.random() * 100 );
Ext.get('testDiv').getWidth(); //make sure the wrong value is cached.
document.querySelector("#testDiv").style.width = radius + 'px';
console.log('I should NOT have the correct value of ' + radius + ' (race start): ' + Ext.get('testDiv').getWidth());
setTimeout(function () {
console.log('I should have the correct value of ' + radius + ' (race condition end): ' + Ext.get('testDiv').getWidth());
}, 0);
}, 0);
//some performance testing...
if (false) {
var loopCount = 10000;
var start = window.performance.now();
for (i=0;i<loopCount;i++) {
Ext.DomHelper.append(Ext.getBody(), '<div id="' + i +'" style="border-radius: 10px">observe me!</div>', true);
}
var domDone = window.performance.now();
console.log('creating dom took: ' + (domDone-start));
for (i=0;i<loopCount;i++) {
for (j=0;j<5;j++) {
Ext.get(''+j).getWidth();
}
}
var getterDone = window.performance.now();
console.log('calling getters took: ' + (getterDone-domDone));
var changeDomDone;
setTimeout(function () {
for (i=0;i<loopCount;i++) {
for (j=0;j<5;j++) {
Ext.get(''+i).dom.style.width = j*25 + 'px';
}
}
changeDomDone = window.performance.now();
console.log('changing dom took : ' + (changeDomDone-getterDone));
}, 0);
}
}
});
</script>
</html>
<!DOCTYPE html>
<html>
<head>
<title>ExtJS Cache Example</title>
<script src="../../src/mutation-summary.js"></script>
<link href="http://cdn.sencha.com/ext/gpl/4.2.0/resources/css/ext-all.css" rel="stylesheet" />
<script src="http://cdn.sencha.com/ext/gpl/4.2.0/ext-all-debug.js"></script>
<body>
</body>
<script>
/**
* this file shows an example of caching the width and height attributes of DOM elements
* in ExtJS when calling the getters and clearing the cache with the setters AND with the
* MutationSummary library to detect attribute changes.
*
* This has the potential to be a little race-condition-y but demonstrates the concept and gaps well enough.
*
* Mutation Summary: https://code.google.com/p/mutation-summary/
*/
Ext.application({
name : 'Fiddle',
launch : function() {
Ext.define('Rally.ui.overrides.Element', {
override: 'Ext.dom.Element',
cachedHeight: null,
cachedWidth: null,
_clearCache: function () {
this.cachedHeight = null;
this.cachedWidth = null;
},
setWidth: function (width) {
this._clearCache();
this.callParent([width]);
},
setHeight: function (height) {
this._clearCache();
this.callParent([height]);
},
getWidth: function () {
if (this.cachedWidth) {
Ext.log('cached w');
} else {
this.cachedWidth = this.callParent([]);
Ext.log('non cached w');
}
return this.cachedWidth;
},
getHeight: function () {
if (this.cachedHeight) {
Ext.log('cached h');
} else {
this.cachedHeight = this.callParent([]);
Ext.log('non cached h');
}
return this.cachedHeight;
}
});
//while this code demonstrates the capability of the MutationSummary library to identify and clear cached DOM
// data, there is a possibility of race conditions since this library summarizes changes, and a better
// implementation might be the native MutationObserver.
var observer = new MutationSummary({
callback: function (mutations) {
mutations.forEach(function(mutation) {
if (mutation.valueChanged.length > 0) {
//look up the Ext dom element, Ext maintains a cache of dom elements by ID
mutation.valueChanged.forEach(function(changedDomElement) {
var extDomElement = Ext.get(changedDomElement.id);
if (extDomElement._clearCache) {
extDomElement._clearCache();
}
});
}
});
},
rootNode: Ext.getBody().dom,
queries: [
{ attribute: 'style' }
]
});
var testDiv = Ext.DomHelper.append(Ext.getBody(), '<div id="testDiv">observe me!</div>', true);
testDiv.setStyle('border-radius', '10px');
testDiv.setStyle('border', '3px solid #BADA55');
//sets the cache
console.log('Default width: ' + testDiv.getWidth());
//change the native DOM style - Ext knows nothing about this, but the mutation observer does
testDiv.dom.style.width = 450;
console.log('I still have the old value even though the DOM is at 450: ' + testDiv.getWidth());
testDiv.setWidth(450);
console.log('I now have the right value (450) since we used a setter to set the size: ' + testDiv.getWidth());
//let the MutationSummary library detect the DOM change and invalidate our cache
setTimeout(function () {
var radius = Math.floor(Math.random() * 100 );
document.querySelector("#testDiv").style.width = radius + 'px';
console.log('I should NOT have the correct value of ' + radius + ' (race start): ' + Ext.get('testDiv').getWidth());
setTimeout(function () {
console.log('I should have the correct value of ' + radius + ' (race condition end): ' + Ext.get('testDiv').getWidth());
}, 100);
}, 1000);
}
});
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment