Permalink
Browse files

Use unload+polling to trigger removal of old <link> [#198]

Previously we had a fixed 200ms delay before removing the old
stylesheet on live CSS refresh.  Problem is that Sprockets asset
pipeline often takes more time to produce a CSS file, causing a flash
of unstyled content for Rails developers.

Most browsers have recently started to support onload event for CSS,
so it makes sense to switch to that, plus polling every 50 ms.

Tested in Safari, Chrome, Firefox and Opera. (Current stable Safari is
the only major browser that uses polling; Safari 5.2 on 10.8 should
support onload too.)
  • Loading branch information...
1 parent 8b091bb commit 959397756dc7b5fdfc9dd6ad53b3714bc2da2bed @andreyvit andreyvit committed May 21, 2012
Showing with 9,199 additions and 15 deletions.
  1. +39 −15 src/reloader.coffee
  2. +9,157 −0 test/html/delayed/test.css
  3. +3 −0 test/html/delayed/test.html
View
@@ -194,6 +194,35 @@ exports.Reloader = class Reloader
return
+ waitUntilCssLoads: (clone, func) ->
+ callbackExecuted = no
+
+ executeCallback = =>
+ return if callbackExecuted
+ callbackExecuted = yes
+ func()
+
+ # supported by Chrome 19+, Safari 5.2+, Firefox 9+, Opera 9+, IE6+
+ # http://www.zachleat.com/web/load-css-dynamically/
+ # http://pieisgood.org/test/script-link-events/
+ clone.onload = =>
+ console.log "onload!"
+ @knownToSupportCssOnLoad = yes
+ executeCallback()
+
+ unless @knownToSupportCssOnLoad
+ # polling
+ do poll = =>
+ if clone.sheet
+ console.log "polling!"
+ executeCallback()
+ else
+ @Timer.start 50, poll
+
+ # fail safe
+ @Timer.start @options.stylesheetReloadTimeout, executeCallback
+
+
reattachStylesheetLink: (link) ->
# ignore LINKs that will be removed by LR soon
return if link.__LiveReload_pendingRemoval
@@ -202,28 +231,23 @@ exports.Reloader = class Reloader
clone = link.cloneNode(false)
clone.href = @generateCacheBustUrl(link.href)
- timeoutElapsed = no
-
- removeOldLinkIfThatsAgreeable = ->
- return if !link.parentNode
- if timeoutElapsed or (clone.readyState is 'complete')
- link.parentNode.removeChild(link)
- clone.onreadystatechange = null
-
- clone.onreadystatechange = removeOldLinkIfThatsAgreeable
-
# insert the new LINK before the old one
parent = link.parentNode
if parent.lastChild is link
parent.appendChild(clone)
else
parent.insertBefore clone, link.nextSibling
- # remove the old LINK even if we don't get the completion event
- timer = new @Timer ->
- timeoutElapsed = yes
- removeOldLinkIfThatsAgreeable()
- timer.start(@options.stylesheetReloadTimeout)
+ @waitUntilCssLoads clone, =>
+ if /AppleWebKit/.test(navigator.userAgent)
+ additionalWaitingTime = 5
+ else
+ additionalWaitingTime = 200
+
+ @Timer.start additionalWaitingTime, =>
+ return if !link.parentNode
+ link.parentNode.removeChild(link)
+ clone.onreadystatechange = null
reattachImportedRule: ({ rule, index, link }) ->
Oops, something went wrong.

0 comments on commit 9593977

Please sign in to comment.