@@ -116,20 +116,13 @@ sub find_xpath           { my( $elt, $path)= @_; return xp->find( 
 sub matches              { my( $elt, $path)= @_; return xp->matches( $elt, $path, $elt);        }
 sub set_namespace        { my $elt= shift; xp->new->set_namespace( @_); }
 
-{
     my %roots;
-    sub DESTROY {
-        my $self = shift;
-        delete $roots{$self};
-        $self->SUPER::DESTROY if $self->can("SUPER::DESTROY");
-    }
 
     sub getParentNode
     { my $elt= shift;
       return $elt->{_parent} if $elt->{_parent};
       return $roots{$elt} //= bless { _root => $elt }, 'HTML::TreeBuilder::XPath::Root';
     }
-}
 
 sub getRootNode
   { my $elt= shift;
@@ -207,21 +200,35 @@ sub string_value 
 # called on a parent, with a child as second argument and its rank as third
 # returns the child if it is already an element, or
 # a new HTML::TreeBuilder::XPath::Text element if it is a plain string
+    my %text_children;
+
+sub DESTROY {
+    my $self = shift;
+    delete $text_children{$self};
+    delete $roots{$self};
+    $self->SUPER::DESTROY if $self->can("SUPER::DESTROY");
+}
+
 sub _child_as_object
   { my( $elt, $elt_or_text, $rank)= @_;
     return undef unless( defined $elt_or_text);
     if( ! ref $elt_or_text)
       { # $elt_or_text is a string, turn it into a TextNode object
-        $elt_or_text= bless { _content => $elt_or_text, _parent => $elt, }, 
-                            'HTML::TreeBuilder::XPath::TextNode'
-                      ;
-        Scalar::Util::weaken($elt_or_text->{_parent});
+          if(!defined $text_children{$elt}{$rank} ||
+             ($elt_or_text= $text_children{$elt}{$rank}, $elt_or_text->{_rank} != $rank || $elt_or_text->{_parent} != $elt)) {
+              $elt_or_text = ($text_children{$elt}{$rank} =
+                              bless { _content => $elt_or_text, _parent => $elt, },
+                              'HTML::TreeBuilder::XPath::TextNode')
+                  ;
+              Scalar::Util::weaken($elt_or_text->{_parent});
+          }
       }
     if( ref $rank) { warn "rank is a ", ref( $rank), " elt_or_text is a ", ref( $elt_or_text); } 
     $elt_or_text->{_rank}= $rank; # used for sorting;
     return $elt_or_text;
   }
 
+
 sub toString { return shift->as_XML( @_); }
 
 # produces better looking XML
 
 
 #########################
 
-use Test::More tests => 31;
+use Test::More tests => 34;
 BEGIN { use_ok('HTML::TreeBuilder::XPath') };
 
 #########################
@@ -37,6 +37,10 @@ is( $html->findvalue( '//*[@id="foo"]/@id|//*[@id="foo"]/@class'), 'myspanfoo', 
 is( $html->findvalue( '//*[@id="foo"]/@class|//*[@id="foo"]/@id'), 'myspanfoo', '2 atts on same element (unsorted)');
 
 is( $html->findvalue( 'count(/|/)' ), 1, 'count roots');
+is( $html->findvalue( 'count(/html/body/h1/text()|/html/body/h1/text())' ), 1, 'count identical text nodes');
+is( $html->findvalue( 'count(id("bq")/@id|id("bq")/@id)' ), 1, 'count identical attribute nodes');
+
+is( $html->findvalue( '//text()[contains(., "folks")]/preceding-sibling::*[1]' ), 'all', 'previous sibling on a text node');
 
 is( $html->findvalue( '//b'), 'boldall', '2 texts');
 is( join( '|', $html->findvalues( '//b')), 'bold|all', '2 texts with findvalues');