Hi Graydon, In general, as Liam has already outlined, all updates refer to the original node. Your specific question could be answered with… $test update { .//span[empty(@*)] ! (replace node . with .//text()) } …but I guess this is not what you need. Instead of a recursive solution, fn:while-do can be used to do repeated updates: while-do( $test, fn($r) { exists($r//span[empty(@*)]) }, fn($r) { $r update { .//span[empty(@*)] ! (replace node . with ./node()) } } ) Hope this helps, Christian ________________________________________ Von: Graydon Saunders via BaseX-Talk <basex-talk@mailman.uni-konstanz.de> Gesendet: Montag, 27. April 2026 23:26 An: BaseX Betreff: [basex-talk] update depth? Hello! If I try to get rid of nested inline markup with no attributes let $test as element() := <p>First <span><span><span>word</span></span></span> was excited.</p> return $test update {(.//span[empty(@*)]) ! (replace node . with ./node()) } I get <p>First <span><span>word</span></span> was excited.</p> Which tells me (or at least I think it tells me) that the outermost replace happens last and the result is replacing the outer span with its node children. I'm not getting free recursion by matching all the span elements. What I want is <p>First word was excited.</p> where all the span elements with no associated attributes unwrap. Is there a plausibly elegant way to do this? (Stacking the same update several times works, and a fully recursive typeswitch approach works, but I can't avoid thinking there's a better way.) Thanks! Graydon -- Graydon Saunders | graydonish@fastmail.com<mailto:graydonish@fastmail.com> Þæs oferéode, ðisses swá mæg. -- Deor ("That passed, so may this.")