|Photo by Jason Pearce|
Some key elements in this document have
<xf:instance id="my-book"> <book id="simple_book"> <chapter id="chapter_1"> <title>Chapter 1</title> <para>Hello world!</para> </chapter> <chapter id="chapter_2"> ... </chapter> </book> </xf:instance>
idattributes which identify the element they are placed on. This means that if you have the value of an id, you can find the associated element. For example the following XPath expression finds the second
The downside of using plain XPath here is that if you have a very large document, the expression might have to scan large portions of the document, possily the entire document. This can be costly. In theory, the XPath engine can back the XML document with indexes, but this is typically done only by XML databases such as eXist.
//*[@id = 'chapter_2']
However XPath specifies an
getElementById()function now implemented natively (and efficiently) by web browsers.
id()function is not strictly required to be faster than a plain document scan, but it can obviously be backed by an index (a hash map), and this is exactly how we just implemented it!
The index is enabled only for a specific instance with:
When support is enabled for an instance, all ids are indexed the first time the
<xf:instance id="my-book" xxf:index="id">
id()function is called. Then, for each mutation of the document (insertion, deletion, replacement, change of value), the index is updated.  At this time, only elements with a local name of
idare indexed. This covers plain
You can use the
id()function this way:
This XForms action inserts a new empty
<xf:insert ref="id('chapter_2', instance('my-book'))" origin="xf:element('chapter', xf:attribute('id', 'chapter_3'))"/>
chapterelement with id
chapterelement with id
Form Builder benefits from this change a lot, because:
- The entire form being edited is stored in an XForms instance.
- All controls and other important elements are assigned ids.
- Finding elements by id is a very common operation.
Until this change, calling
id()on regular (not readonly) instances in Orbeon Forms always returned an empty result. However, calling it on readonly instances already behaved as expected, as we are relying on the Saxon
TinyTreeimplementation for readonly instances. ↩
The implementation is just slightly non-trivial, because the spec says that when more than one element has a given id, the first in document order must be returned. So we must properly track multiple ids for that reason. ↩
id()must support any attribute or element with type
xs:IDbut Orbeon Forms doesn’t implement this yet. In the future, we might also support indexing elements by other aspects, such as CSS classes or element names, as HTML does. ↩