You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When you have time, can you setup a contributing guide? If you're amenable, I'd like to send in a PR for discrete style tags for components. But I don't want to sacrifice the performance in terms of payload size and speed you're shooting for.
I ask because I like this method. I like it a lot. The downside I can see, though, when trying to utilize this with any sort of long-lived client session, is that styles are smashed together into a single style head. Later updates add a new style head. With long-lived clients and frequent CSS mutations, you can find yourself with hundreds, sometimes thousands of DOM nodes whose CSS contents are later overridden (and overridden n-1 times, where n is the number of unique props you send to x-style.
(Another issue is that there is the potential for a flash for unstyled content. But that doesn't seem to happen for me on WebKit, but YMMV.)
I have a patch that adds a new configuration property (and should leave the rest alone) that enables discrete style elements in the head for each DOM element. Is this a good idea? I don't know, TBH. But it makes it easier for this library to manage the lifecycle of individual DOM nodes and which styles we need to keep in the DOM.
In lieu of contributing directly, I mess with a lot of side projects that are UI-only SPAs (all data is stored in the browser). So I took x-style and made it do what I needed in long-lived browser client world. Take these ideas/this code directly if you want! It's gratis. Or throw it away 😄
Caveat emptor: I wrote this for me, and without robust testing, I'm hesitant to add it to this straight to the repo. In addition, I'm not sure if my cleanup method or use of browser Set objects is a good idea for performance. I'm also not super sure if my test is an accurate reflection of real-world performance issues. I might have just spent an hour on this for no real reason 🤷 (It was fun, tho!)
I have not done any golfing with this version. A run through a minifier gets it to about 1.67kb ungzipped, which is 896 bytes gzipped. (Compared to before: 895/564, so about a 332 byte increase (~60%). This is fine for me, but might be a bit too much for this library (unless the minifier I used ain't so hot).
Updated version with DOM style tag management
(()=>{// Aliases to aid in minification:vardoc=document;varquerySelectorAll=(e,s)=>e.querySelectorAll(s);// Plugins, list of functions that take the css and return a new cssvarpluginsPre=[];varpluginsPost=[];/** * x-style * @param {string} attr - HTML attribute that contains the css, usually "css" * @param {boolean} [noMutate] - Don't mutate the DOM by adding attributes * @param {boolean} [discreteStyles] - Saves css in a discrete style element per DOM element */varxstyle=(attr,noMutate,discreteStyles)=>{varstyleEl;varstyle=[];varselectorCount=0;varattributeForSelector=`${attr}-match`;varelementIdAttribute=`${attr}-id`;varelementIdSeed=0;varprocessedCss=newMap();// Map<cssString, renderSeed>varallProcessedCss=newMap();// Map<cssString, renderSeed>varelementProcessedCss=newMap();// Map<elementId, cssString>varelementIdNodes=newMap();// Map<elementId, node>varelementIdStyleNode=newMap();// Map<elementId, styleNode>varidsToUpdate=newSet();// DOM elements that have new CSS to applyvarstyleElementsToDelete=newSet();// We store these in a separate buffer to minimize DOM operationsvarremovingDomElements=false;if(discreteStyles){// Every 10 seconds, clean up our processed CSS map to free up memorysetInterval(()=>processedCss.clear(),10000);}varremoveStaleStyleElements=()=>{if(removingDomElements)return;removingDomElements=true;requestAnimationFrame(()=>{constelements=Array.from(styleElementsToDelete.values());styleElementsToDelete.clear();removingDomElements=false;for(constelofelements){el.remove();}});};varobserver=newMutationObserver((mutations)=>{for(varmutationofmutations){if(mutation.type==="attributes"){processEl(mutation.target);}elseif(mutation.type==="childList"){for(varelofmutation.addedNodes){if(!(elinstanceofHTMLElement))continue;if(el.hasAttribute(attr)){processEl(el);}[...querySelectorAll(el,`[${attr}]`)].forEach(processEl);}if(discreteStyles){for(varelofmutation.removedNodes){if(!(elinstanceofHTMLElement))continue;varid=el[elementIdAttribute];if(!id)continue;elementProcessedCss.delete(id);elementIdNodes.delete(id);idsToUpdate.delete(id);conststyleNode=elementIdStyleNode.get(id);if(styleNode){elementIdStyleNode.delete(id);styleElementsToDelete.add(styleNode);}}}}}emitStyle();});varemitStyle=()=>{if(discreteStyles){constvalues=Array.from(idsToUpdate.values());idsToUpdate.clear();for(constidofvalues){varcss=elementProcessedCss.get(id);if(!css){continue;}varstyleNode=elementIdStyleNode.get(id);if(!styleNode){varstyleEl=doc.createElement("style");styleEl.innerHTML=elementProcessedCss.get(id);doc.head.appendChild(styleEl);styleNode=elementIdStyleNode.set(id,styleEl);continue;}styleNode.innerHTML=css;removeStaleStyleElements();}}elseif(style.length){styleEl=doc.createElement("style");styleEl.innerHTML=style;doc.head.appendChild(styleEl);styleEl=null;style="";}};varsetAttribute=(el,rawCss)=>{varselectorAttr=`${attributeForSelector}-${processedCss.get(rawCss)}`;varprop="__"+attributeForSelector;if(el[prop]){el.removeAttribute(el[prop]);}el.setAttribute(selectorAttr,"");el[prop]=selectorAttr;returnselectorAttr;};/** * Process an element. * Extract the css from the attribute and add it to the style element. * If the css has already been processed, either add the attributeForSelector * or do nothing. * The style element is added to the head on the next microtask. * @param {HTMLElement} el */varprocessEl=(el)=>{varrawCss=el.getAttribute(attr);el[elementIdAttribute]=el[elementIdAttribute]||++elementIdSeed;if(discreteStyles){el.setAttribute(elementIdAttribute,el[elementIdAttribute]);}varcss;if(!rawCss||processedCss.has(rawCss)){if(!noMutate){setAttribute(el,rawCss);}return;}processedCss.set(rawCss,++selectorCount);allProcessedCss.set(rawCss,selectorCount);if(noMutate){css=`[${attr}="${CSS.escape(rawCss)}"]`;}else{css=`[${setAttribute(el,rawCss)}]`;}pluginsPre.forEach((plugin)=>(rawCss=plugin(rawCss)));css+=` { ${rawCss} }`;pluginsPost.forEach((plugin)=>(css=plugin(css)));if(!discreteStyles){style+=css;}else{elementProcessedCss.set(el[elementIdAttribute],css);elementIdNodes.set(el[elementIdAttribute],el);idsToUpdate.add(el[elementIdAttribute]);}};querySelectorAll(doc,`[${attr}]`).forEach(processEl);emitStyle();observer.observe(doc.documentElement,{attributes: true,attributeFilter: [attr],childList: true,subtree: true,});};xstyle.pre=pluginsPre;xstyle.post=pluginsPost;xstyle.version="0.0.3";window.xstyle=xstyle;})();
Here's the (React) test bed I used to play around with performance when adding, mutating, and removing lots of dom nodes
Note: the nature of this test means that you will almost certainly crash your browser eventually. My goal was to have x-style not be the cause.
importReactfrom"react";constgetRandomColor=()=>Math.floor(Math.random()*16777215).toString(16);constrandomNumberBetween=(min: number,max: number)=>Math.floor(Math.random()*(max-min+1)+min);exportfunctionChaos(){const[elCount,setElCount]=React.useState(()=>randomNumberBetween(10,50));React.useEffect(()=>{constinterval=setInterval(()=>{setElCount(randomNumberBetween(10,50));},500);return()=>clearInterval(interval);},[]);return(<div>{Array.from({length: elCount}).fill(0).map((_,i)=>(<ChaosParticlekey={i}/>))}</div>);}functionChaosParticle(){const[color,setColor]=React.useState(getRandomColor);React.useEffect(()=>{constinterval=setInterval(()=>{setColor(getRandomColor);},randomNumberBetween(500,2000));return()=>clearInterval(interval);},[]);return(<divx-style={` background-color: #${color}; padding: 5px `}>
I am a random color (#{color})
</div>);}
The text was updated successfully, but these errors were encountered:
When you have time, can you setup a contributing guide? If you're amenable, I'd like to send in a PR for discrete
style
tags for components. But I don't want to sacrifice the performance in terms of payload size and speed you're shooting for.I ask because I like this method. I like it a lot. The downside I can see, though, when trying to utilize this with any sort of long-lived client session, is that styles are smashed together into a single style head. Later updates add a new style head. With long-lived clients and frequent CSS mutations, you can find yourself with hundreds, sometimes thousands of DOM nodes whose CSS contents are later overridden (and overridden
n-1
times, wheren
is the number of unique props you send tox-style
.(Another issue is that there is the potential for a flash for unstyled content. But that doesn't seem to happen for me on WebKit, but YMMV.)
I have a patch that adds a new configuration property (and should leave the rest alone) that enables discrete style elements in the
head
for each DOM element. Is this a good idea? I don't know, TBH. But it makes it easier for this library to manage the lifecycle of individual DOM nodes and which styles we need to keep in the DOM.In lieu of contributing directly, I mess with a lot of side projects that are UI-only SPAs (all data is stored in the browser). So I took x-style and made it do what I needed in long-lived browser client world. Take these ideas/this code directly if you want! It's gratis. Or throw it away 😄
Caveat emptor: I wrote this for me, and without robust testing, I'm hesitant to add it to this straight to the repo. In addition, I'm not sure if my cleanup method or use of browser
Set
objects is a good idea for performance. I'm also not super sure if my test is an accurate reflection of real-world performance issues. I might have just spent an hour on this for no real reason 🤷 (It was fun, tho!)I have not done any golfing with this version. A run through a minifier gets it to about 1.67kb ungzipped, which is 896 bytes gzipped. (Compared to before: 895/564, so about a 332 byte increase (~60%). This is fine for me, but might be a bit too much for this library (unless the minifier I used ain't so hot).
Updated version with DOM style tag management
gzipped version:
Here's the (React) test bed I used to play around with performance when adding, mutating, and removing lots of dom nodes
Note: the nature of this test means that you will almost certainly crash your browser eventually. My goal was to have x-style not be the cause.
The text was updated successfully, but these errors were encountered: