Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Element.before causes element to be moved to the incorrect location #1898

Closed
TomCats opened this issue Feb 20, 2023 · 5 comments
Closed

Element.before causes element to be moved to the incorrect location #1898

TomCats opened this issue Feb 20, 2023 · 5 comments
Labels
bug Confirmed bug that we should fix fixed
Milestone

Comments

@TomCats
Copy link

TomCats commented Feb 20, 2023

In scenarios where Element a shares a parent with Element b, and Element a precedes Element b, calling b.before(a) will cause Element b to be moved to one index greater than intended.

This occurs due to the internals of Node.reparentChild() having removed the node and then the subsequent call to nodes.addAll() using a stale value for index.

Perhaps it would be better to call a.remove() earlier in the process, at some point before the index for b is being determined. As a work-around, I am calling a.remove() prior to calling b.before(a)

Also note: Node.remove() throws a ValidationException if there is no parent to remove it from - for those watching, be aware of the need to check before calling Node.remove() in the case you are exercising the same work-around. (I would have opted for the method to simply do nothing if there is nothing to remove the element)

@jhy
Copy link
Owner

jhy commented Feb 20, 2023

Could you provide an example testcase? I'm not able to repro -- perhaps I have misunderstood, or there is another piece required.

String html = "<div><p>One</p><p>Two</p></div>";
Document doc = Jsoup.parse(html);

Elements ps = doc.select("p");
Element p1 = ps.get(0);
Element p2 = ps.get(1);

System.out.println("P1=" + p1.siblingIndex());
System.out.println("P2=" + p2.siblingIndex());

p2.before(p1);

System.out.println(doc);
System.out.println("P1=" + p1.siblingIndex());
System.out.println("P2=" + p2.siblingIndex());

Produces:

P1=0
P2=1
<html>
 <head></head>
 <body>
  <div>
   <p>Two</p>
   <p>One</p>
  </div>
 </body>
</html>
P1=1
P2=0

Which looks correct to me. If I add extra elements within the div, it still looks right.

@jhy jhy added the needs-more-info More information is needed from the reporter to progress the issue label Feb 20, 2023
@TomCats
Copy link
Author

TomCats commented Feb 20, 2023

Your example demonstrates the bug perfectly. The elements should not have swapped positions with that API call - they should have remained as they were since p1 was already before p2 to begin with. Call p2.before(p1) a second time and they'll swap places again.

@jhy
Copy link
Owner

jhy commented Feb 20, 2023

Ah, I anchored myself into thinking about the API backwards. It is:

Insert the specified node into the DOM before this node (as a preceding sibling).

@jhy jhy added bug Confirmed bug that we should fix and removed needs-more-info More information is needed from the reporter to progress the issue labels Feb 20, 2023
@jhy jhy closed this as completed in 62de0a1 Feb 21, 2023
jhy added a commit that referenced this issue Feb 21, 2023
@jhy jhy added this to the 1.16.1 milestone Feb 21, 2023
@jhy jhy added the fixed label Feb 21, 2023
@jhy
Copy link
Owner

jhy commented Feb 21, 2023

Thanks, fixed. The output of the above sample is now correct:

P1=0
P2=1
<html>
 <head></head>
 <body>
  <div>
   <p>One</p>
   <p>Two</p>
  </div>
 </body>
</html>
P1=0
P2=1

Also, I made it a no-op vs a validation error to call node.remove() on an orphan node.

@TomCats
Copy link
Author

TomCats commented Feb 21, 2023

Excellent. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Confirmed bug that we should fix fixed
Projects
None yet
Development

No branches or pull requests

2 participants