FluentDOM icon indicating copy to clipboard operation
FluentDOM copied to clipboard

Cannot replace root elements in html-fragment

Open shtse8 opened this issue 7 years ago • 6 comments

<p>Paragraph 1</p> <p>Paragraph 2</p><p>Paragraph 3</p>

As there is no root, how can I search the p and replace it? I tired to use filter and replace, but it will remove all my matched elements.

$dom = FluentDOM::QueryCss('<p>Paragraph 1</p> <p>Paragraph 2</p><p>Paragraph 3</p>', 'html-fragment');
$dom->filter('p')->first()->each(function($element) {
	$element->replace('hi');
});
echo $dom;

Expected:

hi <p>Paragraph 2</p><p>Paragraph 3</p>

Actual:

 <p>Paragraph 2</p><p>Paragraph 3</p>

shtse8 avatar Mar 01 '17 20:03 shtse8

If you're loading an HTML fragment the top level elements should be automatically selected. Here is a problem with replace() however.

At current implementation required the node to have a parent that is an element node. But in this case it is the document node. I added a test and a fix.

ThomasWeinert avatar Mar 03 '17 14:03 ThomasWeinert

I have tried the new commit. filter seems can search deeply.

Code:

$dom = FluentDOM::QueryCss('<div><b>5</b><p>4</p></div>', 'html-fragment');
$dom->filter('p')->each(function($element) {
	$element->replace('hi');
});
echo $dom;

Expected:

<div><b>5</b><p>4</p></div>

Actual:

hi

In jquery:

var test = $("<div><b>5</b><p>4</p></div>");
test.filter("p").replaceWith('hi');
console.log(test.outerHTML());

Result:

<div><b>5</b><p>4</p></div>

And with the following Code, the result is also wrong:

$dom = FluentDOM::QueryCss('<p>Paragraph 1</p> <p>Paragraph 2</p><p>Paragraph 3</p><div><b>5</b><p>4</p></div>', 'html-fragment');
$dom->filter('p')->last()->replaceWith('hi');
echo $dom;

Expected:

<p>Paragraph 1</p> <p>Paragraph 2</p>hi<div><b>5</b><p>4</p></div>

Actual:

<p>Paragraph 1</p> <p>Paragraph 2</p><p>Paragraph 3</p>hi

shtse8 avatar Mar 03 '17 15:03 shtse8

filter() reduces the current selection. You're filtering the current selection for any node that is a 'p' or has a 'p' descendant. The div has a p descendant so it matches. The div is replaced.

Why don't you use find()?

ThomasWeinert avatar Mar 03 '17 16:03 ThomasWeinert

But the result is different from the jquery version as I posted above.

The reason why I don't use find because I can't find the root elements.

code:

$dom = FluentDOM::QueryCss('<p>Paragraph 1</p> <p>Paragraph 2</p><p>Paragraph 3</p><div><b>5</b><p>4</p></div>', 'html-fragment');
$dom->find('p')->first()->replaceWith('hi');
echo $dom;

Result:

<p>Paragraph 1</p> <p>Paragraph 2</p><p>Paragraph 3</p><div><b>5</b>hi</div>

the first it finds is the p inside the div.

shtse8 avatar Mar 03 '17 16:03 shtse8

Hi, any updates?

shtse8 avatar Mar 19 '17 18:03 shtse8

The actual problem left seems to be with the CSS Selector to Xpath conversion. I added some tests using specific Xpath expressions and it works:

https://github.com/FluentDOM/FluentDOM/commit/a8c925b49933e88810bf6fb3d0e97005480c1767

This is difficult to debug because the conversion is not part of FluentDOM itself and an edge case. I will look into it further but it will take time.

ThomasWeinert avatar Jun 28 '17 21:06 ThomasWeinert