Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Prevent stylesheets from adding twice (or more!) #4

Closed
wants to merge 4 commits into from

4 participants

Adam McArthur Scott Jehl Benjamin-K Christoph
Adam McArthur

The problem:

loadCSS fix by adam mcarthur

This fix removes the existing stylesheet (by checking the href parameter against the one passed to the loadCSS() function) before the new one is added to the DOM.

Adam :)

adammcarth added some commits
Adam McArthur adammcarth prevent stylesheet from adding twice
- at the moment if loadCSS() is called twice (this situation is
perfectly feasible), the stylesheet will also be added twice. This fix
removes the existing stylesheet (by checking the href parameter against
the one passed to the loadCSS() function) before the new one is added
to the DOM.
a554ace
Adam McArthur adammcarth update minified version for new patch 4d3db6b
Adam McArthur adammcarth fix the formatting
2 spaces out please
ed9983a
Adam McArthur adammcarth fix formatting v2 8efb010
Scott Jehl
Owner

Hey, thanks. Can you explain the issue this change addresses? I'm not sure why you're trying to load the same file twice.

Adam McArthur

Sure :smile:

So let's say you have a form that submits though ajax and you want to load a new stylesheet asynchronously if the server hits back with a 200 OK (perhaps some css to format a success message). If the form is submitted twice or more (very feasible in some scenarios) - the stylesheet will be appended to the DOM more than once. Although this shouldn't make a difference to the rendering of the css, appending the same stylesheet to the document more than once seems very wrong.

So no, you'd be out of your mind to add the stylesheet twice like I did in my example above - I just wanted to show that it will be added again without removing the old one first.

The changes I made check each stylesheet's href="" attribute, and if it matches the one specified in the loadCSS() function - it will be removed before it is added again. I was doing some deeper thinking whilst running this morning, perhaps it should check that the media="" attribute is the same too before removing it?

Adam McArthur

Checking the media attribute would get very messy since ss.media = media || "all" was used in a setTimeout() function. You'd have to set something like data-real-media to access the real media value (instead of "only x"), but I don't want to overcomplicate things at this stage. If we were to do that I think it should be a separate PR.

Adam McArthur

How are we feeling @scottjehl?

Scott Jehl
Owner

Thanks for checking in, @adammcarth. We talked it over internally and we don’t feel that this addition meets a common enough use case to include in this simple script. That said, I’d encourage you to keep the fork open if you find it useful! Thanks again for the PR.

Scott Jehl scottjehl closed this
Benjamin-K

Why not just cancelling the second request/change?

Christoph

Great point Benjamin. I think it would be better not to reload already loaded files, that would result in unnecessary HTTP requests.

Adam McArthur

@Benjamin-K @inta I thought for quite a long time about that. Reload the stylesheet or prevent it from adding again?

I settled on reloading it - what if the stylesheet was dynamically modified by an external process? The changes wouldn't be applied if we ignored the request. Whilst most adding it again would probably result in an unnecessary http request most of the time, you have to cater for all scenarios.

Christoph

Yeah, I thought about that, but came to the conclusion that this is a rare use case. In production environments I do not change my stylesheets anyway, as they should be cached. I think this is out of the scope of this script, because you need to clear the cache either if you really want to force reload of a CSS file.

Adam McArthur

True. I'll probably update my branch with that functionality in that case - but I still don't think it will change Scott's mind.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jun 13, 2014
  1. Adam McArthur

    prevent stylesheet from adding twice

    adammcarth authored
    - at the moment if loadCSS() is called twice (this situation is
    perfectly feasible), the stylesheet will also be added twice. This fix
    removes the existing stylesheet (by checking the href parameter against
    the one passed to the loadCSS() function) before the new one is added
    to the DOM.
  2. Adam McArthur
  3. Adam McArthur

    fix the formatting

    adammcarth authored
    2 spaces out please
  4. Adam McArthur

    fix formatting v2

    adammcarth authored
This page is out of date. Refresh to see the latest.
Showing with 32 additions and 22 deletions.
  1. +31 21 loadCSS.js
  2. +1 1  loadCSS.min.js
52 loadCSS.js
View
@@ -4,24 +4,34 @@ loadCSS: load a CSS file asynchronously.
Licensed MIT
*/
function loadCSS( href, before, media ){
- "use strict";
- // Arguments explained:
- // `href` is the URL for your CSS file.
- // `before` optionally defines the element we'll use as a reference for injecting our <link>
- // By default, `before` uses the first <script> element in the page.
- // However, since the order in which stylesheets are referenced matters, you might need a more specific location in your document.
- // If so, pass a different reference element to the `before` argument and it'll insert before that instead
- // note: `insertBefore` is used instead of `appendChild`, for safety re: http://www.paulirish.com/2011/surefire-dom-element-insertion/
- var ss = window.document.createElement( "link" );
- var ref = before || window.document.getElementsByTagName( "script" )[ 0 ];
- ss.rel = "stylesheet";
- ss.href = href;
- // temporarily, set media to something non-matching to ensure it'll fetch without blocking render
- ss.media = "only x";
- // inject link
- ref.parentNode.insertBefore( ss, ref );
- // set media back to `all` so that the styleshet applies once it loads
- setTimeout( function(){
- ss.media = media || "all";
- } );
- }
+ "use strict";
+ // Arguments explained:
+ // `href` is the URL for your CSS file.
+ // `before` optionally defines the element we'll use as a reference for injecting our <link>
+ // By default, `before` uses the first <script> element in the page.
+ // However, since the order in which stylesheets are referenced matters, you might need a more specific location in your document.
+ // If so, pass a different reference element to the `before` argument and it'll insert before that instead
+ // note: `insertBefore` is used instead of `appendChild`, for safety re: http://www.paulirish.com/2011/surefire-dom-element-insertion/
+
+ // DELETE OLD STYLESHEET IF IT ALREADY EXISTS
+ var current_ss = window.document.getElementsByTagName( "link" );
+ for ( var i = 0, ss; ss = current_ss[i]; i++ ) {
+ if ( ss.getAttribute( "href" ) === href ) {
+ ss.remove();
+ }
+ }
+
+ // ADD THE NEW STYLESHEET
+ var ss = window.document.createElement( "link" );
+ var ref = before || window.document.getElementsByTagName( "script" )[ 0 ];
+ ss.rel = "stylesheet";
+ ss.href = href;
+ // temporarily, set media to something non-matching to ensure it'll fetch without blocking render
+ ss.media = "only x";
+ // inject link
+ ref.parentNode.insertBefore( ss, ref );
+ // set media back to `all` so that the styleshet applies once it loads
+ setTimeout( function(){
+ ss.media = media || "all";
+ } );
+}
2  loadCSS.min.js
View
@@ -1 +1 @@
-function loadCSS(e,t,n){"use strict";var r=window.document.createElement("link");var i=t||window.document.getElementsByTagName("script")[0];r.rel="stylesheet";r.href=e;r.media="only x";i.parentNode.insertBefore(r,i);setTimeout(function(){r.media=n||"all"})}
+function loadCSS(e,t,n){"use strict";var r=window.document.getElementsByTagName("link");for(var i=0,s;s=r[i];i++){if(s.getAttribute("href")===e){s.remove()}}var s=window.document.createElement("link");var o=t||window.document.getElementsByTagName("script")[0];s.rel="stylesheet";s.href=e;s.media="only x";o.parentNode.insertBefore(s,o);setTimeout(function(){s.media=n||"all"})}
Something went wrong with that request. Please try again.