27. Cross References
A link to another location within the current AsciiDoc document or in another AsciiDoc document is called a cross reference (also referred to as an xref). To create a cross reference, you first need to define the location where the reference will point (i.e., the anchor). Then, you need to use one of the forms of the inline xref macro to create a reference to that location. From there, you can customize the text of the reference in various ways.
27.1. Automatic Anchors
It’s important to understand that many anchors are already defined for you. Using default settings, Asciidoctor automatically creates an anchor for every section and discrete heading. It does so by generating an ID for that section (or discrete heading) and registering that ID in the references catalog. You can then use that ID as the target of an cross reference.
For example, considering the following section.
= Section Title
Asciidoctor automatically assigns the ID _section_title to this section, which you can then use as the target of an xref to create a reference to this section.
You can also customize how this ID is generated.
Refer to Auto-generated IDs for more information about how Asciidoctor generates these IDs.
If you’re referring to a content element other than a section, you’ll need to define an anchor on that element explicitly. Read on to learn how.
27.2. Defining an Anchor
An anchor (aka ID) can be defined almost anywhere in the document, including on section title, on a discrete heading, on a paragraph, on an image, on a delimited block, on an inline phrase, and so forth.
The anchor is declared by enclosing a valid XML Name in double square brackets (e.g., [[idname]]) or using the shorthand ID syntax (e.g., [#idname]) at the start of an attribute list.
The double square bracket form requires the ID to start with a letter, an underscore, or a colon, ensuring the ID is portable. According to the XML Name rules, a portable ID may not begin with a number, even though a number is allowed elsewhere in the name. The shorthand form in an attribute list does not impose this restriction.
If you want to reference a block element, all you need to do is assign an ID to that block. You can enclose the ID in double square brackets:
[[notice]]
This paragraph gets a lot of attention.
or use the shorthand ID syntax:
[#notice]
This paragraph gets a lot of attention.
You can also define an anchor anywhere in content that receives normal substitutions (specifically the macro substitution). You can enclose the ID in double square brackets:
[[bookmark-a]]Inline anchors make arbitrary content referenceable.
or using the shorthand ID syntax.
[#bookmark-b]#Inline anchors can be applied to a phrase like this one.#
In addition to being able to define anchors on sections and blocks, anchors can be defined inline where ever you can type normal text (anchors are a macro substitution). The anchors in the text get replaced with invisible anchor points in the output.
For example, you would not put an anchor in front of a list item:
[[anchor-point]]* list item text
Instead, you would put it at the very start of the text of the list item:
* First item
* [[step2]]Second item
* Third item
Here’s how you can define the ID for a section using an inline anchor:
=== Version 4.9 [[version-4_9]]
To add additional anchors to a section, place them in front of the title.
=== [[current]]Version 4.9 [[version-4_9]]
| If you add additional anchors to a section title, make sure you also assign an explicit ID to that section. Otherwise, the anchor tag gets caught up in the generated ID. See #840 for details. |
Remember that inline anchors are discovered where ever the macro substitution is applied (e.g., paragraph text). If text content doesn’t belong somewhere, neither does an inline anchor point.
It’s possible to customize the text that will be used in the cross reference link (called xreflabel).
If not defined, Asciidoctor does it best to find suitable text (the solution differs from case to case).
In case of an image, the image caption will be used.
In case of a section header, the text of the section’s title will be used.
To define the xreflabel, add it in the anchor definition right after the ID (separated by a comma).
[[tiger-image,Image of a tiger]]
.This image represents a Bengal tiger also called the Indian tiger
image::tiger.png[]
Instead of the bracket form, you can use the macro anchor to achieve the same goal.
anchor:tiger-image[Image of a tiger]
27.3. Internal Cross References
In Asciidoctor, the inline xref macro is used to create cross references (also called in-text or page citations) to content elements (sections, blocks, or phrases) that have an ID (regardless of whether that ID is explicit or auto-generated).
You create a cross reference by enclosing the ID of the target block or section (or the path of another document with an optional anchor) in double angled brackets.
The section <<images>> describes how to insert images into your document.
The section Images describes how to insert images into your document.
You can also link to a block or section using the title by referencing its title, referred to as a natural cross reference. The title must contain at least one space character or contain at least one uppercase letter. (If you are using Ruby < 2.4, that uppercase letter is restricted to the basic Latin charset).
Refer to <<Internal Cross References>>.
Refer to Internal Cross References.
Converters usually use the reftext of the target as the default text of the link. When the document is parsed, attribute references in the reftext are substituted immediately. When the reftext is displayed, additional reftext substitutions are applied to the text (specialchars, quotes, and replacements).
You can override the reftext of the target by specifying alternative text at the location of the cross reference. After the ID, add a comma and then enter the custom text you want the cross reference to display.
Learn how to <<link-macro-attributes,use attributes within the link macro>>.
Learn how to use attributes within the link macro.
You can also use the inline xref macro as an alternative to the double angled bracket form.
Learn how to xref:link-macro-attributes[use attributes within the link macro].
27.4. Validating Internal Cross References
Asciidoctor provides limited support for validating internal cross references. Validation occurs when a cross reference is first visited. Since there are still some references aren’t stored in the parse tree (such as an anchor in the middle of a paragraph), which can lead to false positives, these validations are hidden behind a flag.
You can enable validation of cross references from the CLI by passing the -v and from the API by setting the $VERBOSE variable to true.
This puts the processor in pedantic mode.
In this mode, the parser will immediately validate cross references, issuing a warning message if the reference is not valid.
Consider the following example:
See <<foobar>>.
[#foobaz]
== Foobaz
If you run Asciidoctor in verbose/pedantic mode on this document, it will send the following warning message to the logger.
asciidoctor: WARNING: invalid reference: foobar
Asciidoctor only validates references within the same document (after includes are resolved).
27.5. Customizing the Cross Reference Text
Starting in Asciidoctor 1.5.6, when you use one of the native converters (HTML, PDF, and EPUB3), you can customize the style of the automatic cross reference text using the xrefstyle document attribute.
This customization brings the cross reference text formatting from the DocBook toolchain to the native Asciidoctor converters.
By default, the cross reference text matches the title of the referenced element. For example, if you’re linking to a section titled “Installation”, the text of the cross reference link appears as:
Installation
If the reftext attribute is specified on the referenced element, that value is preferred over its title. For example, let’s assume the section from the previous example was written as:
[reftext="Installation Procedure"]
=== Installation
In this case, the text of the cross reference link appears as:
Installation Procedure
Attribute references are substituted in the reftext during parsing and reftext substitutions (specialchars, quotes, and replacements) are applied to the value when it’s used during conversion.
If the reftext is not specified, the text of the cross reference is automatically generated. By default, this text is the title of the reference. There are three built-in styles you can choose from to customize the generated text of a cross reference, as controlled by the xrefstyle document attribute.
- :xrefstyle: full
-
Uses the signifier for the reference (e.g., Section) followed by the reference number and emphasized (chapter or appendix) or quoted title (e.g., Section 2.3, “Installation” or Figure 1, “Big Cats”).
- :xrefstyle: short
-
Uses the signifier for the reference (e.g., Section) followed by the reference number (e.g., Section 2.3 or Figure 1).
- :xrefstyle: basic
-
Uses the title only (Installation or Big Cats), applying emphasis if the reference is a chapter or appendix.
This formatting only applies to references that have both a title and number (or explicit caption), but no explicit reftext. If the reference is a chapter or an appendix, the title is displayed in italics instead of quotes (even when the xrefstyle is basic).
Let’s assume you want to reference a section titled “Installation” that has the number 2.3. The full style is displayed as:
Section 2.3, “Installation”
The short style is displayed as:
Section 2.3
The basic style is displayed as:
Installation
The full and short styles only apply for references that have a caption.
Specifically, the corresponding -caption attribute must be set for the target’s block type (e.g., listing-caption for listing blocks, example-caption for example blocks, table-caption for tables, etc.).
Otherwise, the *basic style is used.
You can use document attributes to customize the signifier that is placed in front of the reference’s number. This reference signifier indicates the reference’s type (e.g., Chapter or Section).
-
chapter-refsig— defines the signifier to use for a cross reference to a chapter (default: Chapter) -
section-refsig— defines the signifier to use for a cross reference to a section (default: Section) -
appendix-refsig— defines the signifier to use for a cross reference to an appendix (default: Appendix)
(The signifier attribute for a part cross reference will be introduced once numeration is supported for parts).
For example, to customize the word “Section”, define the section-refsig attribute in the document header:
:section-refsig: Sect.
The full xrefstyle would then be displayed as:
Sect. 2.3, “Installation”
The short xrefstyle would be displayed as:
Sect. 2.3
If you unset the attribute, the signifier is dropped from the cross reference text. For example:
:!section-refsig:
In this case, the full xrefstyle will display only the number and title:
2.3, “Installation”
The short xrefstyle will fall back to the number only:
2.3
The basic xrefstyle is unaffected by the value of the signifier.
Only the aforementioned styles are provided out of the box. Support for a custom formatting string is planned. Refer to #2212 for details. Until then, you can implement custom formatting in a custom converter or overridding the xreftext method on the node.
27.6. Inter-document Cross References
The inline xref macro can also link to IDs in other AsciiDoc documents. This eliminates the need to use direct links between documents that are coupled to a particular converter (e.g., HTML links). It also captures the intent of the author to establish a reference to a section in another document.
Here’s how a cross reference is normally defined in Asciidoctor:
The section <<images>> describes how to insert images into your document.
This cross reference creates a link to the section with the ID images.
Let’s assume the cross reference is defined in the document document-a.adoc. If the target section is in a separate document, document-b.adoc, the author may be tempted to write:
Refer to link:document-b.html#section-b[Section B] for more information.
However, this link is coupled to HTML output. What’s worse, if document-b.adoc is included in the same document as document-a.adoc, the link will refer to a document that doesn’t even exist!
These problems can be alleviated by using an inter-document xref:
Refer to <<document-b.adoc#section-b,Section B>> for more information.
The ID of the target is now placed behind a hash symbol (#).
Preceding the hash is the name of the reference document (the file extension is optional).
We’ve also added a label since Asciidoctor cannot (yet) resolve the section title in a separate document.
When Asciidoctor generates the link for this cross reference, it first checks to see if document-b.adoc is included in the same document as document-a.doc. If not, it will generate a link to document-b.html, intelligently substituting the original file extension with the file extension of the output file.
<a href="document-b.html#section-b">Section B</a>
If document-b.adoc is included in the same document as document-a.doc, then the document will be dropped in the link target and look like the output of a normal cross reference:
<a href="#section-b">Section B</a>
Now you can create inter-document cross references without the headache.
27.6.1. Navigating Between Source Files
In certain environments, such as a web interface for a source repository (e.g., GitHub.com) or the browser preview extensions, you see the generated HTML when you visit the URL of the AsciiDoc source file. This has consequences for inter-document cross references.
Since the default suffix for inter-document cross references in the html5 backend is .html, the resulting link created in these environments ends up pointing to non-existent HTML files.
In this case, you need to change the inter-document cross references to refer to other AsciiDoc source files instead.
The file extension chosen for inter-document cross references is controlled by the relfilesuffix attribute.
By default, this attribute is not set and the value of the outfilesuffix is used instead.
If you want to change the file extension that gets used, you can do so by setting the relfilesuffix attributee.
The following example demonstrates how to use the relfilesuffix attribute to control the file extension for inter-docuemnt cross references on GitHub and the browser preview extension.
= Document Title
ifdef::env-github,env-browser[:relfilesuffix: .adoc]
See the <<README.adoc,README>>.
We could also write the link as link:README{relfilesuffix}[README].
This configuration is not actually necessary on GitHub and GitLab since those environments now set the value of relfilesuffix to match the file extension of the source file.
However, it may still be required in other GitHub-like environments, so it’s worth noting.
|
The links in the generated document will now point to README.adoc instead of the default, README.html.
While relfilesuffix gives you control over the end of the resolved path for an inter-document cross reference, the relfileprefix attribute gives you control over the beginning of the path.
When resolving the path of an inter-document cross reference, if the relfileprefix attribute is set, the value of this attribute gets prepended to the path.
Let’s look at an example of when these two attributes are used together.
A common practice in website architecture is to move files into their own folder to make the path format agnostic (called “indexify”). For example, the path filename.html becomes filename/ (which targets filename/index.html). However, this is problematic for inter-document cross references. Any cross reference that resolves to the path filename.html is now invalid since the file has moved to a subfolder (and thus no longer a sibling of the referencing document).
To solve this problem, you can define the following two attributes:
:relfileprefix: ../
:relfilesuffix: /
Now, the cross reference <<filename.adoc,Label>> will resolve to ../filename/ instead of filename.html.
Since this change is specific to the website architecture described, you want to be sure to only set these attributes in that particular environment (either using an ifdef directive or via the API).