'How do ASP.NET Core's "asp-fallback-*" CDN tag helpers work?
I understand what the asp-fallback-*
tag helpers do. What I don't understand is how.
For example:
<link rel="stylesheet"
href="//ajax.aspnetcdn.com/ajax/bootstrap/3.3.5/css/bootstrap.min.css"
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
asp-fallback-test-class="sr-only"
asp-fallback-test-property="position"
asp-fallback-test-value="absolute" />
This loads bootstrap from the CDN, and loads the local copy if the CDN is down.
But how does it decide to do that? I assume it checks asp-fallback-test-class
, asp-fallback-test-property
, and asp-fallback-test-value
. But what do those attributes mean?
If I want to hook up some other library off a CDN, I'll need to supply something for those, but I'm not sure what to put there.
There are lots of examples of this in action, but I can't find explanations about how this works.
UPDATE
I'm not really trying to understand how the tag helpers work - how they render, and so on. I'm trying to understand how to choose values for those attributes. For example, the jQuery fallback script usually has asp-fallback-test="window.jQuery"
which makes sense - it's a test to see if jQuery has loaded. But the ones I've shown above are quite different. How does one choose them? If I want to use some other CDN delivered library, I'll need to specify values for those attributes... what would I use? Why were those ones chosen for bootstrap?
UPDATE 2
To understand how the fallback process itself works, and how those tags are written, see @KirkLarkin's answer. To understand why those test values were used, see my answer.
UPDATE 3
In bootstrap 5 the sr-only
class was renamed to visually-hidden
.
Solution 1:[1]
TL;DR:
- A
<meta>
tag is added to the DOM that has a CSS class ofsr-only
. - Additional JavaScript is written to the DOM, which:
- Locates said
<meta>
element. - Checks whether said element has a CSS property
position
that is set toabsolute
. - If no such property value is set, an additional
<link>
element is written to the DOM with ahref
of~/lib/bootstrap/dist/css/bootstrap.min.css
.
- Locates said
The LinkTagHelper
class that runs against your <link>
elements inserts a <meta>
element in the output HTML that is given a CSS class of sr-only
. The element ends up looking like this:
<meta name="x-stylesheet-fallback-test" content="" class="sr-only" />
The code that generates the element looks like this (source):
builder
.AppendHtml("<meta name=\"x-stylesheet-fallback-test\" content=\"\" class=\"")
.Append(FallbackTestClass)
.AppendHtml("\" />");
Unsurprisingly, the value for FallbackTestClass
is obtained from the <link>
's asp-fallback-test-class
attribute.
Right after this element is inserted, a corresponding <script>
block is also inserted (source). The code for that starts off like this:
// Build the <script /> tag that checks the effective style of <meta /> tag above and renders the extra
// <link /> tag to load the fallback stylesheet if the test CSS property value is found to be false,
// indicating that the primary stylesheet failed to load.
// GetEmbeddedJavaScript returns JavaScript to which we add '"{0}","{1}",{2});'
builder
.AppendHtml("<script>")
.AppendHtml(JavaScriptResources.GetEmbeddedJavaScript(FallbackJavaScriptResourceName))
.AppendHtml("\"")
.AppendHtml(JavaScriptEncoder.Encode(FallbackTestProperty))
.AppendHtml("\",\"")
.AppendHtml(JavaScriptEncoder.Encode(FallbackTestValue))
.AppendHtml("\",");
There are a few things of interest here:
- The last line of the comment, which refers to placeholders
{0}
,{1}
and{2}
. FallbackJavaScriptResourceName
, which represents a JavaScript resource that is output into the HTML.FallbackTestProperty
andFallbackTestValue
, which are obtained from the attributesasp-fallback-test-property
andasp-fallback-test-value
respectively.
So, let's have a look at that JavaScript resource (source), which boils down to a function with the following signature:
function loadFallbackStylesheet(cssTestPropertyName, cssTestPropertyValue, fallbackHrefs, extraAttributes)
Combining this with the last line of the comment I called out earlier and the values of asp-fallback-test-property
and asp-fallback-test-value
, we can reason that this is invoked like so:
loadFallbackStylesheet('position', 'absolute', ...)
I won't dig into the fallbackHrefs
and extraAttributes
parameters as that should be somewhat obvious and easy to explore on your own.
The implementation of loadFallbackStylesheet
does not do a great deal - I encourage you to explore the full implementation on your own. Here's the actual check from the source:
if (metaStyle && metaStyle[cssTestPropertyName] !== cssTestPropertyValue) {
for (i = 0; i < fallbackHrefs.length; i++) {
doc.write('<link href="' + fallbackHrefs[i] + '" ' + extraAttributes + '/>');
}
}
The script obtains the relevant <meta>
element (it's assumed to be directly above the <script>
itself) and simply checks that it has a property of position
that is set to absolute
. If it does not, additional <link>
elements are written to the output for each fallback URL.
Solution 2:[2]
Ok I think I get it now, by combining @KirkLarkin's answer and common sense.
The sr-only
is applied to a hidden meta
element. If bootstrap is loaded then that element would get a css value of position:absolute
. So that is tested, and if it's so, then it means Bootstrap has been loaded.
So for any library, you need to choose a good example of something only that library can do, and style a hidden <meta>
tag accordingly, then specify which css style to test, and what value you are expecting.
For javscript it's even easier, because you can just test for the library itself, which usually has some well known variable added to the window
or something to the DOM. So for jQuery it's window.jQuery
, and for Bootstrap it can be tested as window.jQuery && window.jQuery.fn && window.jQuery.fn.modal
and so on.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Kirk Larkin |
Solution 2 | lonix |