TextFragment
Scroll-to-text URLs: deep-link to a phrase rather than to an anchor, in a page whose markup you don't control.
@inject Bit.Butil.TextFragment textFragmentMDN reference
:~: is a fragment directive, which the browser strips off the
URL while navigating - it is gone from location.hash and from
location.href before page script ever sees it, and
document.fragmentDirective deliberately exposes nothing. So the directive of the page
you are on cannot be read back: Parse reads a URL you still hold, before you navigate
to it. And because the directive is only acted on during a navigation,
Navigate is what makes the browser scroll.
::target-text pseudo-element.
Reports document.fragmentDirective. A false doesn't make your links unusable - they just load the page without scrolling - so this is a 'will the highlight happen' check, not a gate on generating them.
@inject Bit.Butil.TextFragment textFragment
var supported = await textFragment.IsSupported();Only Start is required. Start plus End matches a whole range - the way to link to a paragraph without putting it all in the URL. Prefix and Suffix disambiguate without being highlighted themselves: they say what must sit either side of the match. Matching ignores case, collapses whitespace and only ever matches visible text.
var url = await textFragment.BuildUrl("/text-fragment",
new TextFragmentDirective { Start = "fragment directive" });
// a range:
new TextFragmentDirective { Start = "Only Start", End = "visible text." }Select some text on this page and press the button - this is the same feature as the browser's own 'Copy link to highlight'. A short selection becomes an exact match; a long one becomes a start/end range, so the URL stays a reasonable length without matching any less precisely.
Bit.Butil.Clipboard clipboard
Bit.Butil.Location location
Bit.Butil.TextFragment textFragment
<button @onclick="CopyLink">Copy a link to the selection</button>
{
private async Task CopyLink()
{
var directive = await textFragment.FromSelection();
if (directive is null) return; // nothing selected
var currentUrl = await location.GetHref();
var url = await textFragment.BuildUrl(currentUrl, directive);
await clipboard.WriteText(url);
}
}Reads the directives back out of a URL - the inverse of BuildUrl. It takes a URL rather than reading the current one because the browser strips the directive off while navigating, so the page you are on no longer carries it. Parse a link before following it: to show what it will highlight, to rewrite it, or to expand the collapsed section that contains the phrase.
var directives = await textFragment.Parse(url);
if (directives.Length > 0) ExpandSectionContaining(directives[0].Start);API reference
ValueTask<bool> IsSupported()ValueTask<string> Build(params TextFragmentDirective[] directives)ValueTask<string> BuildUrl(string url, params TextFragmentDirective[] directives)ValueTask<TextFragmentDirective[]> Parse(string url)ValueTask<TextFragmentDirective?> FromSelection()ValueTask Navigate(string url, bool replace = false)