Auch die Leerzeichen vor einem Inline-Element sind prüfenswert. Insbesondere um zu sehen, ob keines vergessen wurde. Eine naive Herangehensweise wäre bspw. ausgehend vom Inline-Element zu prüfen, ob ein Vorgänger Textknoten mit einem Leerzeichen abschliesst:
ends-with(preceding-sibling::text()[1],' ')Plain Text
Dabei werden aber nur die Text-Vorgängerknoten in der Zeile betrachtet, etwaige Inline-Elemente werden aussen vor gelassen. D.h. der folgende Para würde nach diesem XPath Ausdruck noch als gültig erkannt, da nach dem "Hallo " ein Leerzeichen steht.
<p>Hallo <b>fetter Text</b><link>link text</link>Plain Text
Offensichtlich fehlt aber ein Leerzeichen vor dem <link> -Element.
Um zu prüfen, ob der unmittelbare Vorgängerknoten ein Textknoten ist und ob dieser mit einem Leerzeichen abschliesst, kann man diesen XPath Ausdruck verwenden:
ends-with(preceding-sibling::node()[1][self::text()],' ')Plain Text
Oder klarer:
preceding-sibling::node()[1][self::text()]/ends-with(.,' ')Plain Text
Da wir aber bisher immer nur die preceding-sibling Achse betrachten, entgehen uns Leerzeichen, die in zuvor gesetzten Inline-Elementen vorkommen, bspw. hier:
<p>Hallo <b>fetter Text </b><link>link Text</link></p>Plain Text
Deshalb müsste man eigentlich den XPath Ausdruck noch erweitern:
preceding-sibling::node()[1][self::* or self::text()]/ends-with(string(.),' ')Plain Text
Dabei ist zu beachten, dass der string()-Cast auch noch verschachtelte Inline Strukturen flachklopfen würde.
Jetzt könnte man sich denken, dass man ja eigentlich diesen Ausruck verkürzen könnte zu:
preceding-sibling::node()[1]/ends-with(string(.),' ')Plain Text
Dann würde aber auch die folgende Zeile erfolgreich geprüft:
<p>Hallo <b> fetter Text <i>Hallo</i></b><!-- Hallo --><link>link Text</link></p>Plain Text
Denn der Kommentar schliesst mit einem Leerzeichen ab. Auch Kommentare und Processing Instructions sind vom Typ node() .