Still on Drupal 7? Security support for Drupal 7 ended on 5 January 2025. Please visit our Drupal 7 End of Life resources page to review all of your options.Problem/Motivation
Currently, Drupal.AjaxCommands.prototype.insert() always wraps the returned HTML in a div. There are multiple conditions where even the more limited DIV wrapping creates problems (see comments #41-#48).
See the consequences of this in massive duplication related issues, and on screens in #331, and on your sites.
Proposed resolution
The solutions avoids wrapping returned content in all cases where it's possible without breaking existing sites. We will still wrap the inserted markup when trying to insert multiple root elements with a ajax effect. This will be removed from Drupal 9.0.0. For more details: #284. However, the div is now added in a theme function which provides an option for developers to opt out from this.
We can't parse SVGElement without wrapper since innerHTML is only available on HTMLElement. See #316 - #321 about it.
As a result, we decided to first insert the markup into an empty div, and then use .contents() to get the elements (see #336):
// Parse response.data into an element collection.
const $newContent = $('<div></div>').html(response.data).contents();
// ...
// Add the new content to the page.
$wrapper[method]($newContent);
See latest patch
Remaining tasks
- 🎉™️
| Comment | File | Size | Author |
|---|---|---|---|
| #394 | interdiff-736066-391-394.txt | 1.46 KB | GrandmaGlassesRopeMan |
| #394 | 736066-394.patch | 30.78 KB | GrandmaGlassesRopeMan |
| #391 | 736066-391.patch | 29.32 KB | alexpott |
| #391 | 387-391-interdiff.txt | 888 bytes | alexpott |
| #387 | interdiff.txt | 4.05 KB | lauriii |
Comments
Comment #1
casey commented+1 We don't need to solve bugs of browsers nobody uses any more.
Comment #2
yched commented+1 - marked #818670: Can we remove the wrapping div added by ajax 'insert' commands ? as duplicate
Comment #3
aspilicious commentedWell I agree. And google recently announced dropping the support for chrome 3.0 so chrome is definitely safe. And I couldn't find any recent browser statistics about safari 3.0, so that version must be dead by now :)
Comment #4
yched commentedAfter some testing,
var new_content = $(response.data);does not work when response.data is some HTML with a text node at the toplevel :-
response.data = '<span>foo</span>bar'--> only
<span>foo</span>gets inserted-
response.data = '<span>foo</span>bar<span>baz</span>'--> JS execution stops during
new_content.hide();a couple lines below.It does seem like a wrapping tag is needed. In this case, we should make sure it is removed when we're done inserting and applying effects.
Attached patch does that.
Comment #5
yched commentedrecategorize
Comment #6
katbailey commentedAt a glance, and without having tested it at all, it looks like this will cause problems when Drupal.attachBehaviors is called. As its first parameter it's expecting a jQuery wrapped set to use as the context, but it could end up getting something like
<span>foo</span>bar<span>baz</span>. If I'm right, then you'll need to defer removing the temporary wrapper until after Drupal.attachBehaviors is called. I'll try and test if I get a chance later today...Comment #7
seutje commentedis there any reason we can't retain the wrapper and make it's display property match the parent's?
Comment #8
sunI was under the assumption that top-level text nodes were fixed/supported in jQuery 1.4 now, but it seems I got that wrong.
If I'm not mistaken, then we have another issue regarding #wrapper in the queue that might be closely related.
Comment #9
yched commented@seutje : there are contexts where the mere presence of the div messes your layout :
It can also cause CSS rules to break.
This div just means you don't control the HTML you're outputting, which is really embarrassing.
Comment #10
yched commented@katbailey #6:
Hm. Animations being asynchronous, removing the div after both animations and behaviours attachment are done might be tricky.
Is the current order (animate, attach behaviors) a requirement, or could we attach behaviors before showing the new content ?
Comment #11
casey commentedI think the whole Drupal.ajax.prototype.commands.insert() function is weird. Can't we split it up into the following?
insert() // add response.data into wrapper using .html()
empty() // remove children of wrapper
replace() // replace wrapper using .replaceWith()
remove() // already exists
the method .replaceAll() seems useless in this context; it allows to replace/move the wrapper to response.data (expected to be a selector string)
Comment #12
katbailey commentedI'm using the ajax link example from ajax_example module to test this, and the issue of context being passed to Drupal.attachBehaviors is proving difficult to test due to this weirdness: #825318: Drupal.attachBehaviors() is called an extra time, unnecessarily and for document context, when there is no form.
@yched, are you testing this with ajax forms?
Comment #13
yched commented@katbailey: Yes, testing with a form. I have to say I didn't really look closely at the behaviors nor noticed they got fired twice.
I'm not sure it's really related to the current issue, but it does make things a little confusing as to how to properly fix the extra div issue :-/.
Comment #14
effulgentsia commentedFrom #4:
That's a shame, but if it's true, then I don't think a temporary wrapper that gets removed is the right answer, because of the issue in #6. What about adding an AJAX command setting (or another property attached to the AJAX command object being returned) for letting people turn off the wrapper, so people can do so when they know they're returning a well-formed fragment (like something already in a 'td')?
Comment #15
casey commentedWhat about this?
Minor regression is that if there are no ".ajax-new-content" elements and an effect is being used, text nodes at top-level will be show immediately (without the effect).
Comment #16
yched commented@eff #14
What about the following workflow ?
- attach behaviours (done as a last step currently)
- fire animations
- remove the wrapping div in a callback at the end of the animations
Animations are asynchronous anyway, so even in current HEAD, behaviours get attached on dom elements while they're being animated. I don't think it would make a difference for behaviors if they were attached *before* the animations ?
Comment #17
casey commentedBehaviors might check for visible elements only...
Comment #18
effulgentsia commentedI'm not enough of a JS / jQuery expert to know if this gets us into the trouble that katbailey warns about in #6 when new_content is then used as the context in the attachBehaviors() that follows. How can we find out?
Re #16:
A common use-case for attaching behaviors is wysiwyg editors, which use IFRAMEs. DOM manipulation (like removing a container DIV) of elements containing IFRAMEs causes the IFRAME to reload which messes up the editor, so we should absolutely not do any DOM manipulation after behaviors have been attached.
65 critical left. Go review some!
Comment #19
casey commentedComment #20
yched commented#19 doesn't answer effulgentsia's remark in #18 : won't behaviors end up with context being a mix of tags and top level text nodes, as pointed in #6 ? - although this hasn't yet been proved to be an actual issue
A couple thoughts :
- would it be valid to pass the 'target' as context for behaviors in all cases ?
(currently, it's the target if method is 'replaceWith' / 'replaceAll', and the content of the target if method is 'html')
- could a solution lie around the idea of adding a wrapper div only if response.data doesn't contain a wrapping tag itself (i.e
<div>a</div><div>b</div>or<div>a</div>b<div>c</div>) ?Comment #21
casey commentedComment #22
effulgentsia commented@yched:
great idea!
@casey: great implementation!
So how about making this issue just that, as in this patch, and putting the effects code cleanup of #21 into a separate issue for further discussion?
Comment #23
yched commented- I'm wondering about the case where content.data has several top-level tags (i.e
<div>a</div><div>b</div>). For those, #21 and #22 remove the wrapper as well, and animations and behaviors are attached to a jq object with n elements, instead of currently on the elements as a whole. Shouldn't we also keep the div in those cases ?- minor : is there a way we could create new_content without the div and add it if needed, rather than adding it and removing it just after that ?
+ when there are no text nodes, children() might be more explicit than content() ?
from http://api.jquery.com/contents :
"The .contents() and .children() methods are similar, except that the former includes text nodes as well as HTML elements in the resulting jQuery object."
Comment #24
effulgentsia commentedWith #23.
Comment #25
effulgentsia commentedActually, I'm not sure if #24 works right (see #4).
Comment #26
effulgentsia commentedTested it and #24 does not work. This goes back to #22 but adds the consideration from #23 that our effects code currently assumes a single element (at least when there isn't an ajax-new-content class in the content).
Not that I can think of. I added a comment in the patch that explains why not. But according to http://api.jquery.com/jQuery/#creating-new-elements, jQuery itself often implements $(HTML_STRING) as creating a temporary DIV and removing it.
Well, the
ifstatement must use .contents() since it doesn't know yet if there are text nodes. So while inside theifcould be done with .children(), it seems to me that sticking with .contents() makes more sense.Comment #27
effulgentsia commentedI think the code comments in #26 makes this clear, but just wanting to address this question from #23 head on:
This should not be a problem for behaviors (it's possible there are broken behaviors in contrib that rely on the context to be a single element, but that's a broken behavior). But it is a problem for the effects code as it is currently written. We could try to make the effects code more robust, as per #21, but I continue to think that should be a separate issue. #26 takes into account the currently delicate effects code while still solving the primary use-case this issue started with.
Comment #28
yched commentedAgreed on the remarks in #26 / #27. Patch looks good to me.
Casey, what do you think ?
Comment #29
casey commentedYes looks good to me too; nice and clear comments. One thing though; I'd add contents() to a var:
Comment #30
effulgentsia commentedComment #31
yched commentedYay.
Comment #32
casey commentedStill applies.
Comment #33
mh86 commentedthis patch helps a lot when working with the ajax system (e.g. for appending another row to a table) and still applies.
Comment #34
dries commentedIs this meant to be a permanent solution? It looks like it is a bit of a hack. Maybe we should add a @todo for Drupal 8?
Comment #35
sunAFAICT, this is a permanent solution, as long as AJAX accepts any kind of returned value, string, or fully-fledged HTML structure, and as long as the corresponding server-side calling code does not preemptively specify the nature/structure of the delivered content upfront. It is hard to tell whether this could be improved in any way.
Comment #36
merlinofchaos commentedhttp://drupal.org/node/919994 is a dup of this. FYI, this functionality is critical to the CTools D7 port.
Comment #38
sun#30: ajax-commandinsert-736066-30.patch queued for re-testing.
Comment #39
dries commentedCommitted to CVS HEAD. Thanks.
Comment #41
manu manuA note to googlers or those who followed the link from misc/ajax.js:501 and that have some trouble inserting several table rows in a HTML table, like this:
You will need only one top-level dom element in
$htmlotherwise the rows will be wrapped into a<div>element.Having several
<tbody>is OK, so you can do something like this:Comment #42
OnkelTem commentedWell, aren't we care about inserting DIVs into HEAD?
When a stylesheet is returned with the 'insert' command with data like:
this results to wrapping inside the 'div' and placing it into the 'head' element because of this code:
The reason is:
1) In the above example, the style element for some reason is suffixed with a newline, which creates two elements in the
new_contentvariable: style element and text node, so making the if-expression to trigger on .length check (which = 2)2) If we were get more then 1 stylesheet from a server, then amount of elements are simply more then 1, working out the same condition.
I don't know how bad it is to have divs in head, but I personally dislike it.
I propose not to use DIVs at all, when the wrapper is set to 'head':
Comment #43
hrmoller commentedI think the general problem with this code is that it possibly produces invalid HTML.
E.g. if your PHP code produces a bunch of
<li>-elements to be inserted into an existing<ul>, the<li>'s will get wrapped inside a<div>which is not allowed at top-level in<ol>/<ul>, following the W3-definitionIdeally it should be possible to pass a flag to indicate whether you want your content wrapped or not. That flag could be added to the the response object in PHP and checked for in ajax.js:
PHP:
And in ajax.js, line 489:
Note: This might be too much of a hacky solution and it should rather be seen as inspiration...
Comment #45
greg boggsHere's a jQuery work around to remove the invalid divs from a given #selector without affecting the contents.
http://stackoverflow.com/questions/6428254/removing-only-the-parent-elem...
Comment #46
lmeurs commentedA side effect of the committed patch of #30 is that the inserted wrappingDIV's can get nested endlessly.In our case we use the Form API's managed file field in a custom form. When uploading or removing a file, file_ajax_upload() returns multiple HTML elements on each AJAX call, these elements get wrapped and the nesting begins.
EDIT1: I think the File module probably should not append extra elements outside the AJAX wrapper, see #2302011: Managed file elements get wrapped + nested on each AJAX file upload.
EDIT2: Why not add ie. the
.ajax-new-content-wrapperclass to thenew_content_wrappedelement so it is easier to identify?Comment #47
dcam commentedThe code added by #30 still exists in 8.x. If there is an issue with it then it will have to be fixed there first.
Comment #48
andypostno patch for 8.0.x
Comment #49
effulgentsia commentedRaising to Major, because multiple issues are running into different flavors of this same underlying bug, and because per #43, producing invalid HTML is bad.
Adding the "JavaScript" tag for obvious reasons :)
Also updated the summary.
Comment #50
fubhy commentedMany false positives on this check:
can be circumvented by simply str.trim()'ing the response.data to ensure that jQuery does not do weird things in .html() resulting in the creation of empty text nodes (I think this occurs when there is a "\n" at the end or beginning of the response data).
This solved it for me:
it doesn't really fix the underlying problem though...
Comment #51
andypostNot sure that
trim()is a nice way to clean-up response that could be big enough.I still think the batter to make that at the server side
Comment #52
guedressel commentedThe fact that I can't inject multiple javascript files via ajax into a page makes me sad :-(
In commet #42 this side-effect is explained already, here my use case for JavaScript files:
In a ajax callback I add multiple javascript files via drupal_add_js(), which results in a ajax-insert-command with multplie <script> tags. Those are then added to <head>, wrapped in a <div>, and that for not interpreted by my browser.
I'll be happy if any solution makes it into 7.x upstream.
Comment #53
guedressel commentedI've to take back my critisism a little: even tough my scripts are wrapped by a <div> they get interpreted by my browser. Hence not so sad anymore :-|
Comment #54
steamx commentedJust wanted to "push" this issue as nothing has been done to fix this yet. It is highly annoying for me to work with D8 Ajax while theming. As there will be an RC out there soon I'd request at least a workaround e.g. have a constructor parameter for new AjaxCommand(..., $wrapper = FALSE) where $wrapper would decided about doing wild div stuff.
Comment #55
tim.plunkettI don't see how it affects Usability (which is the tag we use, not UX). Fixing the DX tag.
Comment #56
steamx commentedIn my terms UX => User experience because
Comment #57
andypostComment #58
ericduran commentedSo an empty div is always being appended now since all render function seem to be returning an \n.
I haven't tracked this down fully but for now he's a hacked up patch.
I'm undecided if the new line should be fixed here or in the theme. This JS is too sensitive right now which is why I'm doing it here. It might also be fair to just removed the need to check if there's multiple elements on the root of the doc. I'll look into this further.
Posting patch for now to move forward on my issue.
Comment #59
andypost@ericduran thanx for bumping and idea about trimming with right way!
Please elaborate on
That's now obvious that trimming should be done after render, maybe that's cheaper to do at the backend, because some requests could be big.
Comment #60
meaton commentedThank you very much!
Comment #61
zviryatko commentedAlso it doesn't work when twig.config.debug is enabled.
Comment #62
wim leersThis is most likely also causing problems for BigPipe, because BigPipe just reuses the AJAX system: #2654386: [PP-1] BigPipe uses AJAX system to insert new content, AJAX system adds a wrapping <div>, breaks theme.
Comment #63
wim leersNow effectively confirmed. Which means this potentially blocks BigPipe from being added to Drupal 8.1, see #2469431-204: BigPipe for auth users: first send+render the cheap parts of the page, then the expensive parts.
Comment #64
tic2000 commentedWhat about this solution?
It allows the code to behave the same if nothing changes. But on top of that allows the developer to set another wrapper or force not using a wrapper at all.
Comment #67
tic2000 commentedComment #68
tic2000 commentedThis should fix the tests
Comment #72
spheresh commentedPatch #58 works fine, but it needs backport to D7
Comment #73
tic2000 commentedI talked with @nod_ on IRC and took a look on jQuery to see what changed since the launch of D7 when was the last time I checked this issue.
Looking at http://api.jquery.com/Types/#htmlString we have
Which means we could remove all the code with new_content and new_content_wrapped and do
But the issue then is on the following code, not the actual insert.
new_content.find('.ajax-new-content')will throw an error on text nodes.If we do
var new_content = $($.parseHTML(response.data));thenDrupal.attachBehaviors(new_content.get(0), settings);will run only for the first node and throw an error if the first node is a text node. We can fix that with:I imagine this can raise some performance concerns. But if you want to insert multiple table rows or list items now you need to send multiple AJAX Commands which would be even slower.
To fix this we have decide what path we take.
My opinion is that we should not wrap in a div. The developer should have full control of what the result of the command is. Of course with freedom should come responsibility too.
Also note that using $.parseHTML() will not be possible in D7.
Comment #74
lokapujyasomething like this:
bye<b>hello</b>the developer can wrap in a
<div>anyway. But it sounds like this:shouldn't be wrapped in a div (because that would be invalid HTML) Drupal AJAX insert doesn't currently allow it to be not wrapped, but should. However if the
<td>'s are not wrapped in a div, I imagine that would that mean that the jQuery effect would happen to each<td>instead of the whole block of newly inserted<td>'s? Although according to #41, you can get around this by wrapping the<td>'s in a<tbody>.Comment #75
tic2000 commentedSomething like
bye<b>hello</b>could be inserted in a span. But that can be wrapped by the developer too.You can wrap tr in a tbody, not td. And as I said, now the workaround for inserting 2 td's like in your example is to call InsertCommand twice which means everything is run for each td anyway, including the attachment of behaviors.
Comment #76
droplet commentedCan't it ?
EDIT: Ouch, skip this patch
Comment #77
droplet commentedcorrect patch:
Comment #78
tic2000 commentedI investigated the new line issue and after I found why it happens I searched d.o and saw that the issues is actually related to this one. #1237012: An empty status message in the AJAX response is added to the commands[] array and added to the DOM as an empty div tag
Regarding #77, that solution can work for D8 too. The "drawback" is that it can expose any badly written behavior implementation.
HTML output in core

HTML output with patch in #77

Comment #79
hog commentedPatch #68 tested.
Before ajax:

After ajax:

But i have a question: we really need wrap content in div?
Comment #80
hog commentedComment #81
tic2000 commentedA patch using the parent approach. Like I said previously this may make the context become "body" so run for everything on the page exposing behaviors that didn't use .once().
I upload it for testing and review. The comment would need changes for a commit.
Comment #82
andypostComment #83
effulgentsia commentedI haven't manually tested #81 yet, but #73 says:
Is that true? If so, wouldn't #81 trigger that, since that's exactly the code that's running between the removed hunk and added hunk?
Comment #84
tic2000 commentedIn #81 for multiple elements or when the first is a text it sends the parent. Which means instead of creating the div parent we send whatever the parent of the new content is. In the worst case it's "body". Which means if we send 2 table rows the parent will be tbody instead of wrapping them in a div. In 73# I was talking about sending the new content as is.
Comment #85
effulgentsia commentedBut prior to the patch's
new_content = new_content.parent();line, there's existing code in ajax.js that does:So those find()s happen on the sent content, not the parent. Also, if we enter the
elseblock, then we're running an effect on potentially multiple nodes and/or text nodes, and is that ok?Comment #86
tic2000 commentedThe issue about new_content.find() was when I was doing
If I was doing new_content = $($.parseHTML(response.data)) that was not a problem. While I don't parse the HTML in this patch, I also don't remove this part which does the parsing now:
Comment #87
nod_The patch works, making body the context sometimes is fine. Now when using the file upload field before and after we still have leftovers
<span class="ajax-new-content">but not they're not nesting infinitely in divs, they just add up at the same level.Why do we tiptoe around edge cases to begin with? Do we have an exemple of a module that needs whitespaces and textnodes+domelements on the root of the response, and that can't be replaced with something better? I don't see why it's not ok to just say "don't do it anymore" and make a 'text' ajax command that would insert text if needed.
Comment #88
nod_Blast from the past where people had to comment to subscribe :þ Turns out I agreed with tic2000 already #1031600-10: AJAX requests cause duplicate, invalid CSS includes and multi-nested response data output, so I'm obviously ok with this solution.
Comment #89
duaelfrI just got affected by this issue. It took me a long time to understand what was wrong.
Thanks all for your work. I did the manual testing.
@nod_ I think it's too late to break BC, even on frontend, so let's commit this fix then open another issue to discuss about changing how everything works (for D9, maybe).
Comment #90
catch#87/#89 if there are no real examples relying on that behaviour, I think it's OK to change it in a minor release. i.e. the concrete bug in #87 is worth fixing compared to the theoretical regression that might be introduced for code we don't know about.
Comment #91
tic2000 commentedWhat is described in #87 can not be solved by this patch in any way. What is described in #87 is most we can do. There the issue is that we have DOM A, we add to it DOM B, and replace DOM A with A + B. Repeat over and over, so we end up with multiple B.
But I took a look at #2588013: <span class="ajax-new-content" style="display:inline-block;"> causes unwanted whitespace and provided a fix there too. There was another solution already provided, so there is even a choice in what approach we take there.
Comment #92
catchDuaelFR was that an RTBC for #68 or #81?
Comment #93
duaelfrSorry. #81 patch is good to me, according to my tests and the reading of #87
Comment #94
catchOK in that case as #81 says the comment needs updating for the new approach.
Comment #95
droplet commentedSorry! I reviewed it again. I think #85 is a problem for current sites. Anime is changed. Do it in 8.1 is more suitable.
#68 has similar problem but developer can make decision for unexpected anime during development.
What if adding a `unwrap()` as callback ?
** I leaving the debug message there. So if you go to `admin/structure/types/manage/article/form-display` and click config. You can see better ajax result.
- Can we do refactor and break BC for Drupal.AjaxCommands.insert API in D8.1?
- Any popular modules output wired code now?
Comment #96
drintios commentedAdded unwrap if we have node type 1, if we have other node types we have wrapper, and if we have nothing we just return nothing , this might be a good approach to this issue.
Comment #97
drintios commentedComment #99
morsokPatch in #81 needed a reroll. (there was merge conflicts I hope I did it right).
Comment #101
morsokPrevious reroll had errors ($ missing).
Comment #102
morsokComment #103
ajalan065 commentedPrevious patch was throwing some errors.
Uploading the new patch.
Comment #104
droplet commentedthis approach (#103) raised by me and rejected by myself. Sorry :(.
Now we have few approaches
1. @droplet #81 (#103)
Problem: Anime effect is changed. After patching, it's apply anime to each div without single parent root
2. @tic2000 #68
Problem: Similiar to #1, But developers know it's problem on coding
3. @droplet #95
Problem: ( no known issues until this momont )
Comment #106
VinayLondhe commentedPosting reroll patch for ericduran's 736066-56 patch. Disregard this.
Comment #107
wim leersBecause BigPipe uses the AJAX system, this also affects BigPipe: #2738685: BigPipe leaves wrapper <div>s in place, because AJAX system leaves them — this can cause some CSS to break.
Comment #108
Saphyel commentedTesting the previous patch and @VinayLondhe you didn't follow the file name standards.
Comment #109
dom. commentedThis also affect views with AJAX exposed filters. Because of the extra div, the views contextual links (for quick edition) are broken after your "Apply" your filters once and does not show anymore.
Comment #110
dom. commentedSorry, after test of #106: it appears that the div is no more included, although the "contextual links" does not appear anyway.
So this is RTBC, and the contextual links will go to another issue.
Comment #111
fabianx commentedI suggest we start with a functional JS test first to get this issue back on track.
Then we can much more easily see which solutions fix the problem and which don't.
Comment #116
adinac commentedRe-roll for ajax_js_insert_command-736066-101.patch.
Comment #118
samuel.mortenson+ if ($new_content.length !== 1 || $new_content.get(0).nodeType !== 1) {This logic doesn't work when theme debugging is enabled, or in other use cases where comments and whitespace are returned alongside a single Node Element. The debug comments will make
$new_content.lengthgreater than 1, and$new_content.get(0).nodeTypeis either Node.TEXT_NODE (newline) or Node.COMMENT_NODE. This makes development difficult as I have to turn off theme debugging while working on any AJAX logic.I think what we actually want to check for is that there is only one Node.ELEMENT_NODE type element in $new_content, although this logic would require a loop that makes sure that the other siblings are not significant to the user (text nodes with whitespace and comments are probably OK, Node.DOCUMENT* and Node.PROCESSING_INSTRUCTION_NODE are not).
Comment #119
dtamajon commentedI leave a patch with a different approach. It still continues adding the extra DIV, but after the first request it recognizes the previous created DIV and replace it avoiding infinite nested layers.
Comment #120
lokapujyaI forget why we need a DIV. Why couldn't we just add a class to all the new elements being added? Is it because of something like this:
bye<b>hello</b>In other words, what legacy reason?
Comment #121
wim leersThis blocks #2738685: BigPipe leaves wrapper <div>s in place, because AJAX system leaves them — this can cause some CSS to break. Tagging.
Comment #125
tedbowOk. so here is patch that just adds tests. Write now the tests pass without any changes to ajax.js but that is because I am not sure what desired behavior is.
I didn't write the tests using the dialog system but used a direct call to Drupal.ajax().
The 3 tests cases I thought are response of:
<div>text</div>not wrapped textoutside-text<div>text</div>What other test cases should be test for?
Also what should be desired behavior of each?
Comment #126
samuel.mortenson@tedbow Based on my comment in #118, there are (at least) two cases that the committed fix did not cover:
In both cases, with the current logic, a wrapping <div> is added, even though the HTML string only contains one top-level element that is already wrapped. This is explained a bit more in #118.
Comment #127
GrandmaGlassesRopeMan@tedbow Thanks for the tests. This, I think, will address the test cases and wrap things correctly.
Comment #128
tedbow@samuel.mortenson thanks for the clarification.
Do you think the test I added is good way to test the functionality generally or should be just use use-ajax dialog links?
I can add the additional test cases you mentioned.
Comment #129
samuel.mortensonSome review:
if "response.data" included a text node and a dom element, like
"test<b>test</b>", then the returned length from the filter function would be "1", and the response would not be wrapped. I'm not sure what's expected here, but that's an edge case I thought of.I think this could just be $reponseDataWrapped, instead of wrapping the response in another call.
Testing with use-ajax links would be a bit simpler than this, I think.
It would be great if we could get test coverage for leading comments and whitespace as well. Thanks for taking this task on!
Comment #130
samuel.mortenson#129.1 is incorrect, I missed the
$responseDataWrapped.contents().lengthcall.Comment #131
GrandmaGlassesRopeMan@samuel.mortenson Thanks for the review.
U+1F44DI want to possibly circle back to #120 and ask again, why we are even wrapping the response? It feels like we are going to get caught up in a bunch of really complicated logic that will never accurately handle all the possible edge cases that can happen with AJAX'd data.
Comment #132
GrandmaGlassesRopeManAt a high level this will sequentially add non-element nodes (text, comment, etc) from an HTML string until an element node (div, p, etc) is found. At that point, the resulting wrapped non-element nodes and the the element nodes are added to
$new_content. If there are subsequent after the first element node is encountered, the process continues.Comment #134
samuel.mortensonIf $responseDataWrapped.contents() returns a single value with nodeType === 1, $new_content will be empty as "wrapper" is NULL. This should be testable with response like
<div>hello</div>.Comment #135
guypaddock commentedWith #77 applied in D7, it breaks the admin UI for fields on entities.
Comment #136
GrandmaGlassesRopeMan@samuel.mortenson
Yeah. I didn't update the tests to account for the new behavior.
@GuyPaddock
Yeah. I have not been testing this at all in D7. I assume some additional work will need to take place to backport this.
Comment #137
GrandmaGlassesRopeManFixes a few edge case issues and updates the tests.
Comment #138
guypaddock commentedThe error I was seeing in D7 was:
Uncaught TypeError: Failed to execute 'getComputedStyle' on 'Window': parameter 1 is not of type 'Element'. at Ga (jquery.js?v=1.4.4:130) at Function.css (jquery.js?v=1.4.4:128) at $.fn.init.hide (jquery.js?v=1.4.4:147) at Object.insert (ajax.js?v=7.54:536) at Drupal.ajax.success (ajax.js?v=7.54:428) at Object.success (ajax.js?v=7.54:189) at Object.a.success (jquery.form.js?v=2.52:12) at Function.handleSuccess (jquery.js?v=1.4.4:143) at XMLHttpRequest.w.onreadystatechange (jquery.js?v=1.4.4:142)This affects selecting Media in the media browser for an entity that has no images yet, as well as when trying to rearrange fields in Fields UI. It appears that the case it's running into is a case where the content being added ends up being text DOM nodes, which makes the window the target.
The attached patch is a variant of 77 that works around the issue, but seems to break effects.
Comment #139
roborew commentedTest for failure, this code currently causes the issue described, with the ajax wrapper breaking a select list.
Un-commenting the optgroup tag fixes the issue, but then causes problems when using the select list with the chosen module.
Post Patch Tests:
With wrapper already set:
Return without wrapper:
Return string:
Patch works with the expected behaviour for the three scenarios above. Will set this to review and tested, but realise there might be tests required that i might have missed outside of my use case, happy to take further guidance and test more scenarios as needed.
Comment #140
droplet commentedHaving a quick review. It looks very similar / same as #103 but with extra redundant conditions?
Anyway, the content will be lost if data returned something like #95
Missing END
Comment #141
droplet commentedAlso, the same anime issue as I summarized in #104 ?
(If we allowed let it break in D8.3/4, is #103 ready to go?)
Comment #143
GrandmaGlassesRopeMan#139
Was this tested using the patch from #137?
#140
I believe that the patch from #137 would account for this case and not drop the
ENDtext.Comment #144
roborew commentedHey @drpal, yes tests in #139 were made against your patch #137.
Comment #145
dmsmidtI've tested #137 in combination with the Refreshless module. Now I get an empty divs before the inserted content instead of a wrapping div.
The problem stems from indentation. If the response.data contains for example a space before the a div, we get an extra empty div.
We should probably skip text containing only whitespace or linebreaks.
Comment #146
dmsmidtSomething like this works in my case (would need test).
Edit: argggg typ0, working on version with tests
Comment #148
dmsmidtHere is a better version, it also has an extra test for the leading white space case.
Note, I rewrote the test a bit so that it runs faster (less page requests).
As mentioned earlier, we still haven't figured out what to do with theme debug comments. These get wrapped in a div now.
It could also be a follow up IMHO.
Comment #150
droplet commentedone leading or trailing SPACE is validated we cannot trim it?
Comment #151
dmsmidt@droplet, what do you mean?
Are you saying we should allow leading and trailing spaces?
I think it causes too many problems, as can be seen in the Refreshless module. If someone really wants a leading or trailing space in an unwrapped response, he/she could add
.New patch: Removed debug line.
Comment #153
GrandmaGlassesRopeManFixes a minor nit with checking for length from #151. Additionally, I think it's probably a good to handle leading whitespace in this way.
U+1F44DComment #154
droplet commentedHmm. What behaviors do we expect and what will change in the current patch?
For example:
In patch #153
BEGIN <div>testing</div> ENDBecame:
<div>BEGIN </div><div>testing</div>`BEGIN ` was an INLINE element, now this is BLOCK level. And DIV can be styled with CSS.
Comment in the current code:
Is it still true?
Assumed this is FALSE, then we need not do anything with `response.data`?
Comment #155
dmsmidt@droplet,
So what I understood about wrapping the TEXT_NODE / COMMENT_NODE in a div is that we do that because it is needed for animations. My preference would be that we don't wrap (we don't actually need it for comment nodes), but..
We have a bit of a deadlock here:
1. We don't want to change the DOM structure when replacing content, because that can break CSS (
.some-class > .my-div). And can lead to heavy divitis (for example with the Refreshless module).2. We don't want to really wrap text nodes and comment nodes, however it is needed for animation.
3. Currently when the return data consists out of multiple nodes, animation can be weird. For example if you use the slide animation, multiple items will slide instead of one (the later is the current behavior).
We also can't just temporarily wrap multiple nodes so that we have a single animation, and then unwrap after the animation, because styling could in that case break during animation.
My preference would be that we don't try to compensate for multiple animations, in the cases that for example a single slideToggle is needed the returned data should just be wrapped in a single block element. That is up to the implementer.
Even so, the last patch is still not complete.
The code below is responsible for attaching behaviors to the new content.
This only works for a single item.
So when, for example (with the latest patch) we turn theme debugging on, we only attach behaviors for the newly inserted theme debug comment. Ouch!
Comment #156
GrandmaGlassesRopeMan@dmsmidt
Would it be possible for you to update the tests to provide an example where this fails?
Comment #157
dmsmidt@drpal sure, here is a patch with an additional test on top of what we already have.
Comment #159
dmsmidtSo that failed as expected, working on a patch fix.
Comment #160
dmsmidtAttached patch fixes the 'context' problem mentioned above. And tries to fix earlier mentioned problems as well.
So, to answer some questions: text nodes should not be passed to attachBehaviors since jQuery could interpret them as selectors, so we should still wrap those (sadly enough).
Currently we wrap them in a
<div>, this can be destructive for a design since we return a block level item. Returning a should be less destructive to designs, and still gives the necessary design possibilities on inserted elements for themers. This has been changed in the given patch. I think this is not a BC break in practice. I also updated the tests to reflect this, and changed some typ0's.Furthermore, comments should never be wrapped. We also don't need them in 'context' when attaching behaviors. Not wrapping them has huge benefits for when theme debugging is turned on. I changed the patch accordingly and added a test case for that as well.
The wrapping logic has been optimized in the same go. Instead of an integer I use the 'Node.COMMENT_NODE' constant for type checking. This is more readable and works in IE9+.
Lastly I also made the test a bit stricter, now it is checked that the target div is directly wrapping the new data (to ensure no other divs/spans are added).
Comment #161
GrandmaGlassesRopeMan@dmsmidt
Nice work. Thanks for handling that.
Just a slight concern about the variable name here since it's just the number of elements.
Comment #162
dmsmidt@drpal, improved var naming.
Also I found that this wasn't enough when we insert new elements with the replaceWith method.
We should test the different insert techniques that we support.
This version at least tries to make it work with 'replaceWith' next to 'html'.
However.. I don't know what will happen if we use 'replaceWith' with multiple elements.. Needs some work.
Comment #163
dmsmidtScary that those tests pass, since I have some test sites that break hard. Thus adding 'need tests'.
At least the following patch works on an AJAX heavy test site (Refreshless + AJAX views).
I probably don't have time in the next days to work on the needed tests.
The currently added tests in this patch work with the 'html' method. We need to check that context is alright for the different inserting techniques ('replaceWith', 'append', 'prepend', 'before', 'after', or 'html').
Of those I think 'replaceWith' is the most important. The others probably work like 'html'.
Comment #164
pavelculacov commentedlast patch test, work perfect.
after apply patch, on custom block where get rendered View and commerce variante where after ajaxReplace appear empty div.
Thanks @dmsmidt
Comment #165
GrandmaGlassesRopeManRefactored tests, adding support for
replaceWith.Comment #166
dmsmidtI like the rewrite. One discussion point:
This possibly makes testing slower, I'm not sure how significant it is, but we could easily skip it for the 'html' method. For 'replaceWith' it is necessary since the #wrapper will be gone after the first replace.
We could also generate different wrappers for each test type and method. Than we just need to request only a page once.
Any thoughts?
Comment #167
GrandmaGlassesRopeMan@dmsmidt
Thanks. I don't really have an opinion other than it's nice to have more complete coverage.
Comment #168
wim leerswas done by @drpal. Test coverage is present, and looks rather comprehensive.
So… is this then RTBC again?
Comment #169
droplet commentedHmm. with the new behavior, I'm interested what I'm missed in below patch or the test cases don't catch.
(Patch should be failed. I didn't trim the SPACE and remove SPAN in tests)
EDIT:
Just realized the code around isAllElementNode conditional can be simplified. (But with the same logic. I will update if it's right approach)
Comment #170
droplet commentedWith #169,
we able to do further and wrap "NODE_ELEMENT only" response with DIV.
Any "NODE_ELEMENT only" response matches:
It must be safe to ADD A WRAPPER. doesn't it?
Comment #172
droplet commented- Fixed failure tests
- Enhanced the tests
- It seems big_pipe will sent out empty responses.
- In this patch, I introduced a little idea to keep inline element. But the result is the dimension of parent SPAN will be wrong and little semantic problem. However, it will keep single-parent-element result.
Comment #174
dmsmidt@droplet I fear you want to over-optimize, and the big rewrites are a bit hard to follow without a bit more explanation.
We have a working patch in #165, could you tell what your problems are with that approach? Did you actually find problems with that patch?
I'm up for improvements, but it would be great if you could explain them a bit more elaborate so that I can follow.
In #160 I explained the considerations for the last changes before your version. The 'inline-element' styling trick you introduce in #172 I find a bit too much, and out of scope. We already swapped
Comment #175
droplet commented@dmsmidt,
To my understanding, your patches does not fulfill the deadlock your told in #155. But for some reason (technical?), it changed the way to render things.
#165 changed:
1. Adding
SPANto TEXT_NODE2. Anime effect is changed.
3. No more single-parent root.
#172 fixed the bugs and keeps no (lesser) BC layer changes:
1. the dimension of parent SPAN will be wrong and little semantic problem. (Note: rendering is correct.)
2. (Basically, anime is same as now)
3. Still no single-parent root with some cases but we could do it. @see #170
Could you update the test case to make it fail. Thanks.
(Anyway, both patches have their Pros and Cons. I really want to find out if we give it up because of the technical issue or we really want things working that way.)
Comment #176
dmsmidt@droplet, while making the patch of #165 I took the 'deadlock' issues into consideration, and thought about what would be the best way forward.
The patch ensured minimal DOM structure changes, this is the biggest win! Indeed only for root text nodes a SPAN is added. The span is less destructive than DIVS for the layout. We need a wrapper, otherwise we can't pass it as behavior context.
See above, we do need to wrap text nodes, also for
context. Animations I'm not that concerned with anymore. If someone wants a consistent single animation, he/she should wrap the return data in a single node element.Currently AJAX inserts/replaces break the DOM structure. This is more problematic than the rare cases that only root text nodes are returned or the combination of multiple root elements in the return data with a slide effect. I haven't really encountered those edge cases in the wild, but in case they do, functionality doesn't break, only the animation is less pretty.
My questions about #172:
We probably have a language barrier between us, because I don't really understand this ;-) Could you help me?
How can this be? If we want to keep animation as how Drupal works now, we always need a single root element.
One single parent root element is just the things we want to prevent in this patch.
For example, if someone is appending items to a list of items via AJAX, every time we append, there will be an extra nesting (in case we use a single root parent element. This is problematic.
See the above.
Does this answer your question about finding out why certain approaches where used?
Comment #177
wim leersThis was RTBC in #140, on March 4, more than 2 months ago.
Can we please continue here? This has been causing strange bugs for years, and has been causing problems for BigPipe users in particular too: #2738685: BigPipe leaves wrapper <div>s in place, because AJAX system leaves them — this can cause some CSS to break.
Comment #178
dmsmidt#165 was the last completely functional patch as fas as I know. #140 didn't take care of multiple things (the discussion after that is not completely useless). Both @drpal and me are happy with #165, and thought it RTBC worthy. @droplet, then did a major rewrite, but never got passing tests.
Still waiting for his reply for over a month. I'd like to have this fixed a.s.a.p., but I guess we need another person to review the two versions and have an opinion about it.
Comment #179
nod_reviewing
Comment #180
wim leers\o/ Thank you, @nod_!
Comment #181
nod_Both patches had issues, but droplet had the simplest one. The approach was pretty much the same since parseHTML was doing what the previous patch tried to.
Tests pass and more importantly it fixes the issue with the image upload widget.
In any case there are some strange things going on in testing: the test ajax command return 2 command, the 'html' or 'replace' command and a second one with 'prepend'. Don't know where that second one comes from but it's strange.
I did cheat on one test, since the response is trimmed one got rid of one space in the expected data in the test.
Put back the detachbehavior on the wrapper, no idea why that got changed along the way.
He means it's possible for
<div>to end up inside of<span>like with theinlineexemple.Comment #183
GrandmaGlassesRopeManFixes the tests from #181, since we don't return a
divanymore.Comment #184
dillix commentedTested drpal's patch, +1 for RTBC
Comment #185
dmsmidtSome nits and questions.
Please use lowerCamelCased.
Exceeds 80 chars.
Can we be a bit more specific with the description? Also let ppl know we are testing that attaching Behaviors work, this was a simple but important addition I think!
We have some code repetition here (only the wrapper changes).
Add a comment to let others know this is for testing if behaviours work well.
Shouldn't we be replacing these for the assertions that waits for an element?
Help, where and why does this happen again?
Comment #186
GrandmaGlassesRopeManFrom #185
Comment #187
GrandmaGlassesRopeManComment #188
dmsmidtThanks again.
4: ok!
Concerning 3&5: now it is not clear at all why we add the 'processed' class.
I added it because before this patch we just had one node element passed to attachBehaviors, but now we can have multiple. And we want to be sure that they all have been the 'context' for attachBehaviors.
Somehow we should make that clear at least.
I think it is currently the closest thing to behaviors testing we have (although still very simplistic).
Comment #189
didebru#186 does not work for 8.3.1 . Although the file 8.4.x ajax.js and the 8.3.x/1 ajax.js are identical... The patch could not be applied.
Comment #190
nod_yes, this is the usual process. We fix the bug in the dev version, which is 8.4, and if we think it's possible or relevant to backport we do that after it it fixed in the dev version. For now we focus on 8.4.
Comment #191
nod_Just for the record, #1 is scope creep, this is not a clean-up patch.
We seem to be missing a kind of test:
About 3&5 it's less about testing behaviors than making sure attach behaviors are called when ajax content gets inserted. We're already assuming they work. And since we had one type of test missing it didn't make as much sense since we were only dealing with one top-level element in all the other cases.
Comment #192
wim leersI'm so very tempted to RTBC this, this seems so very close! Leaving that to those who've been closely involved lately though. Just wanted to say I'm cheering from the sidelines to see this very tricky, nasty bug nearing its final breath!
Comment #193
nod_Did a bit or refactor to simplify the code a little. We have tests so it's fine, don't worry about it :þ
Remove the 'hack' for the mixed content where the leading whitespace was removed, inserted as-is now.
Another edge case, make sure that whitespaces in the middle of top-level-only nodes wrap the content:
I think we covered everything important?
Comment #194
nod_Reroll for ES6.
Comment #195
droplet commentedOh, we almost patched the whole insert function now. Here's few suggestions (can do in follow-up also):
1. Use ES6 directly in insert function. no var, use const...etc
2.
settings = response.settings || ajax.settings || drupalSettings;move 2 settings to top.
3. caching
$newContent.find('.ajax-new-content')4. ideally, new JS file should be ES6 ready
\core\modules\system\tests\modules\ajax_test\js\insert-ajax.es6.jsComment #196
oriol_e9gMinor:
If we have an span instead of a div the var name should not be: $green_div, $red_div, etc.
Comment #197
wim leers#195: let's do that in a follow-up indeed. Let's first finally land this! :)
#196: nice nitpicks!
Comment #198
droplet commentedLet me reroll it for real quick. Actually, the ES6 give us a clean overview on scopes.
- Reroll
- ES6 syntax on insert and new file
- Fixed #195
- Fixed #196
- Fixed typo.
Comment #200
droplet commentedSame as #198 but remove a non relevant file
Comment #201
GrandmaGlassesRopeMan=>=>Some minor nits about arrow functions.
Comment #202
droplet commentedThanks @drpal. I thought to keep it simple so didn't update functions with this keyword.
@seee new patch. Even cleaner code now :) We know where the `this` from.
Comment #203
dinesh18 commentedAs per the Drupal coding standards we should not use "else if" -- always use elseif.
Comment #204
nod_those are PHP standards :) in js elseif doesn't exists
Comment #205
dinesh18 commentedoops sorry... my bad :(
Comment #206
nod_Reviewed, this is good to go.
Comment #207
oriol_e9gI can still find #196 nitpiks. The var name and texts like "The selected color DIV is green" should be "The selected color SPAN is green".
$green_span
$green_span2
$red_span2
Comment #208
droplet commentedComment #210
dinesh18 commentedI tried to apply the patch #202 and it is failing.
PFA screenshot
Comment #211
wim leers#2880007: Auto-fix ESLint errors and warnings landed, which conflicted with this. This needs a reroll.
Comment #212
dinesh18 commentedAdded needs Re-roll tag
Comment #213
GrandmaGlassesRopeMan- Rerolled due to #2880007: Auto-fix ESLint errors and warnings.
Comment #214
droplet commentedI thought I will mark it RTBC but the logic code contributed by me also. I may not the right person to review it.
Comment #215
wim leersBack to RTBC.
Comment #217
nod_testbot strangeness.
Comment #219
effulgentsia commentedI really like #213 a lot! Great work, everyone! All I could find is nits.
s/need/needs/
s/TEXT_ELEMENT if/TEXT_ELEMENT and/
Clever!
But this means we would wrap
hello <em>world</em>in a DIV instead of a SPAN, even if the target element is not a block element, since there's more than one DOM node. Has this been discussed already? Maybe we should open a followup issue for seeing if we can cover that case? And in any case, I think a comment here for why it's ok or linking to the followup issue would be helpful.s/Wrap/Wraps/
Can we add a comment explaining why it's okay that this returns an incomplete/malformed HTML fragment? I.e., that this function is / should only be called by code that only needs to test a substring and not by any callers that expect well-formed HTML.
Can we also add a test for the 'not-wrapped' render type when targeted into a block-level element?
Comment #220
nod_Fixed all feedback + removed the use strict from the js file (we don't use that since es6).
Can't add a test for 7 since it can't happen, if there is one textnode in the response'll always be a span. we can't have two text nodes from a single string. But I did add a test for when there is a comment + textnode where a wrapping div will be used when a span should have been used.
Comment #221
wim leersComment #222
droplet commentedhas some? It seems not right
Comment #223
nod_At this point in the code, it doesn't matter if we have 1 or 10 comment nodes, it should be in a span. What is important is that we have at least one comment node with our textnode.
Comment #225
droplet commentedWhy has COMMENT_NODE should be SPAN?
Comment #226
nod_It's not only about the comment node:
Before:
After (fixed):
No surprises when adding an comment before/after a string in an ajax response.
Comment #227
droplet commented#226 makes sense. but with .some:
will be
Comment #228
wim leersThis was RTBC for the longest time, and @effulgentsia nearly committed it in #219. @droplet un-RTBC'd, and I see @nod_ explaining to @droplet why it's correct the way it is. So moving back to RTBC.
It'd be great to finally have this fix shipping with Drupal 8.4!
Comment #229
droplet commentedI don't think it's correct and it broke the tests also. How can it RTBC?
the condition should filter the comment node and then count the remaining node is non-block level, not put any response with comment_node into SPAN.
I will RTBC if we drop #220 additional condition.
Comment #230
GrandmaGlassesRopeMan- Very minor adjustments to the tests based on the latest wrapping condition.
@droplet from #229 - Would you consider a followup to this? I do agree that having a
spanpotentially wrapping adivgoes against the spec. It' might be worth it though to get this fix in and address a spec violation immediately after.Comment #231
droplet commented@see below comment
Comment #232
droplet commentedSorry about that took some time on EDITING previous comment. I've done real code testing to ensure I didn't miss any code changes.
Assumed the $wrapper is DIV ( block-level )
A.
Patch #220: DIV
Patch #230: DIV
B.
Patch #220: DIV
Patch #230: SPAN
Then I will ask why A isn't SPAN in Patch #230
---
And I'd like to point out @effulgentsia's this point is wrong (I also overlooked at this point before)
The patch detects the $wrapper over there. If $wrapper is SPAN, then SPAN. If not, then DIV. Therefore, Patch #230 will change a top block-level to inline-level elements.
Thanks.
Comment #233
GrandmaGlassesRopeMan- An adjustment to make a better judgement call about wrapping responses with a
spanor adiv. 🤷♀️Comment #234
tim.plunkettI was reviewing this, and wanted to fix the test to more clearly test each case. Now when a single case fails, you will clearly know which one it was.
I think a couple more cases need to be added to address #232. There is a COMMENT DIV one and a SPAN COMMENT but no COMMENT SPAN.
Comment #235
nod_Thanks @droplet, insightful as usual :)
#233 is what I had in mind too :)
Comment #236
droplet commentedHmmm, it hinted me something new:
if
onlyTextOrCommentNodesmet the condition, then we will have:Before Ajax: content is BLOCK LEVEL
After Ajax: content changed to INLINE LEVEL
After Ajax with replaceWith: (Change from BLOCK -> INLINE)
Therefore, we only need this SINGLE ONE condition?
Comment #237
tim.plunkettThat may be true, but please write the test cases first! If the logic is wrong now, you should be able to come up with cases that fail.
Comment #238
droplet commented- [NEW]: Split tests into INLINE & BLOCK level
- [BUG FIX]: these 3 test cases should get the same result, no wrapped.
- [NEW]: Added `pre_wrapped_div` and `pre_wrapped_span` and dropped `inline` and `pre_wrapped`
This patch should be failed
Comment #239
tedbow@droplet thanks for the new patch.
for those of us that aren't following this issue that closely can you explain why #238 should fail..
Thanks
Comment #241
GrandmaGlassesRopeMan@droplet
Maybe you forgot to add your bug fix?
top-level-only-middle-whitespaceis now being wrapped with a span, which is incorrect.Comment #242
droplet commented@drpal #241,
I didn't add code fix to above patch. Just enhanced the tests. @see below explanation.
@tedbow #239,
** Note 1: Patch #238 changes based on my conclusion of #236.
** Note 2: Don't forget we have `replaceWith` method:: If the script rendered with`html` method, DIV or SPAN usually doesn't matter and I can see both have their Pros & Cons. However, to use `replaceWith` method, we should treat the content same inline/block level as before.
Attached my local test failture log:
A:
Single TEXT_NODE
Patched: SPAN
Expected: depends on $wrapper, it can be DIV or SPAN
B:
Single TEXT_NODE.
Patched: SPAN
Expected: depends on $wrapper, it can be DIV or SPAN
C:
Patched: DIV
Expected: no wrappers, it should treat as top-level-only.
D:
Patched: SPAN
Expected: depends on $wrapper, it can be DIV or SPAN
E:
Patched: SPAN
Expected: no wrappers, it should treat as top-level-only.
Comment #243
droplet commented- Fixed failures
AjaxTest is 100% passed on my local :)
Comment #244
droplet commentedthis fixes the middle space issue
this fixes the empty content
this fixes the single node / comment mixed types
very tired. see all of you tomorrow :p (it's 3 a.m. here)
Comment #246
tim.plunkettI spent a couple hours with @drpal on this today, and I have a few changes.
1)
When the method is 'html', and the new content begins with an HTML tag, no wrapping is needed.
This can be seen in the 'empty' test case.
This contradicts #244.2, but I think this is correct to change.
2)
If the response data has a DIV in it, it shouldn't matter what the destination markup is, it is already block level HTML.
We should do our very best to determine the from the data what the wrapper is. This patch is a *HUGE* step forward at removing needless divs.
But switching based on the destination markup is a mistake IMO.
3)
I removed the WRAPPER_PLACEHOLDER trick and switched each test case to the correct result.
Comment #247
droplet commented@tim.plunkett,
Do you disagree this point?
In patch #246, "Link replaceWith not-wrapped" always wrapped SPAN, I don't think it's correct. (this is also explained in #236)
Comment #248
droplet commentedSo if #246 run with #243 tests,
I have no comments to 3 and 5 (empty), I also plan to remove it, just no time to figure out why we kept it before (e.g. a second action will insert HTML into first .processed). It's a similar issue: #2588013: <span class="ajax-new-content" style="display:inline-block;"> causes unwanted whitespace.
Comment #249
tim.plunkettIn the case of both 1 and 2, the HTML being inserted contain no block level elements. So why wrap in a DIV?
And the opposite is true of 4, the HTML already contains a DIV, so wrapping in a SPAN is pointless.
Comment #250
GrandmaGlassesRopeManFrom #248,
- 1 & 2, lets not add extra divs when we don't need them.
- 4, I think this goes back to wrapping block level elements with inline elements, lets not do that.
➕1️⃣ from me.
Comment #251
droplet commentedYou didn't look at `replaceWith` I think? and my principle is to keep it as same as BEFORE Ajax call as possible as we can.
I really don't know should I call TEXT inline or block level honestly. I could only tell it's whole HTML code is block level.
(I think TEXT NODE has no inline/block concept, only HTML tags had.)
<div>TEXT</div>`replaceWith`, it's clearly the whole HTML code is inline level.
<span>TEXT</span>If previous HTML tag is inline level:
Before Ajax call: DIV is on next line.
<span class="prev"></span><div>TEXT</div>After Ajax call: SPAN is on the same line
<span class="prev"></span><span>TEXT</span>#246.2 agreed on this point I think but the patch showed a different behavior? It's a bit confusing. and SPAN isn't equal to inline-level. that's why I added
$wrapper.css('display') !== 'block'and on my point:
Generally, we wrote CSS (e.g. CSS RESET) this way:
div,
div div,
div + div,
{
color: GREEN
}
span,
span span,
span + span,
{
color: RED
}
Patch #246 changed the text color.
Comment #252
droplet commentedNot sure if it helps:
https://codepen.io/anon/pen/eEWVzw
Comment #253
droplet commentedand it reveals a bug I mentioned in previous comments again. (I wasn't given an example clearly)
SPACE before TEXT & Inline Tags does matter. We cannot trim it.
https://codepen.io/anon/pen/OjmQvE
Comment #254
tedbowFrom #248
1,2 I agree with @tim.plunkett and @drpal I don't see why we would wrap these responses in divs.
3. I also think we should not be wrapping an empty response in a div, so the behavior and test coverage in #246 is correct.
4,5 I don't think we should be looking at the target and have that determine what we wrap the response in. This seems like it really open up a lot edge cases.
Adding tag " Needs issue summary update" because I don't think the summary will help understand the problem and solution.
Comment #255
effulgentsia commentedThis whole business with guessing when to add a wrapper or not, and whether to make it a span or a div just seems plagued by complexity. What if we simplify to never wrap? Here's a patch that does that. Tests will fail, because I haven't adjusted them yet to not expect the wrapper. Would someone else like to do that? If not, I'll try to get to it tomorrow.
I'll also update the issue summary tomorrow for why I think it's ok to never wrap. It does have an impact on how effects are rendered, but I'll write up why I think that's ok, and better than us trying to second guess the response data.
Comment #257
dmsmidtI like the idea of not wrapping, let the implementer make sure he/she has all the wrappings that are needed, for animations this patch already took that approach.
Wrappers where further only needed for easy context handling for behaviors I think. I see you now only pass element nodes as context. As a developer I could live with that, no magically appearing extra elements.
Comment #258
effulgentsia commentedSorry, all. I wasn't able to get this finished today, so I think it'll be 8.5-only at this point.
I'm pulling my hair out with phantomjs. On my machine, even with HEAD, I get the same error for
BigPipeRegressionTestthat's in https://www.drupal.org/pift-ci-job/739001. Which makes me think that something got cached from when I had the patch applied. But I tried reinstalling everything, from the codebase to my local site to my computer, and nothing is fixing it. I'm hoping to make progress on this quickly once I can get my local testing tools working correctly.Comment #259
effulgentsia commentedWhile still working on figuring out my local test environment, let's at least get a DrupalCI run when we undo the accidental change of removing the
trueargument for keeping scripts.Comment #261
maximpodorov commentedTs there the same issue for D7?
Now we have to use the following ugly solution (D7):
if ((new_content.length != 1 || new_content.get(0).nodeType != 1) && (wrapper.index($('head')) < 0)) {
new_content = new_content_wrapped;
}
Comment #262
wim leersCan we please finally bring this to the finish line?
#2913563: Facets blocks loaded with BigPipe wrapped in unwanted <div> due to bug in AJAX system and #2738685-10: BigPipe leaves wrapper <div>s in place, because AJAX system leaves them — this can cause some CSS to break are again reporting this bug anew, and are blaming BigPipe. The root cause is the AJAX system, this issue.
We were so close to getting this committed in August.
Comment #263
andypostRelated #1268180: Newline character in HtmlTag causes unwanted whitespace also can help to remove some noise (for cases when ajax returns element)
Comment #264
droplet commentedI don't support this BC broken actions for D8.4 and the last few patches but if I'm the only blocker here. I will leave this issue to let it go. Thanks all.
EDIT:
my comments #251, #252, #253 has examples to demo the problem of the last patches.
Comment #265
GrandmaGlassesRopeManAlright, an effort to get this rolling again. This patch changes none of the logic from #259, but does fix the test coverage.
Comment #266
GrandmaGlassesRopeManComment #267
wim leers@droplet: so do you prefer @effulgentsia's approach in #255/#259?
Comment #269
droplet commented@Wim Leers,
I haven't reviewed @effulgentsia's patch in #259 carefully yet but the concept is simple. We may think of it too simple.
Let's say a 3000 words article WITHOUT
ptag wrapped for each paragraph. It has many links, color, bolded styles. It easily ends up with a few hundreds top level of tag. I see it as a performance issue. (it'd be a huge impact to big_pipe-like modules I think)Then we have default
$.hide / $.showanime. And it applies to TAGs wrapped contents only. The inline texts are still VISIBLE. It animes around the text. It's really odd. Example: https://codepen.io/anon/pen/OjmQvEFor the latter problem, it able to do one more simple change:
To accept `NULL|'none'` to remove default `$.hide / $.show` effects. (clean up all default anime)
EDIT: I even have not evaluated the jQuery.once / Drupal.attachBehaviors issues
----
With a clearly BC break solution for D8.5, a group decision is better than single one's opinion
Comment #270
GrandmaGlassesRopeManI forgot these changes last time. 🤷♀️
Comment #271
wim leers@effulgentsia in #255:
The code is much simpler indeed. @drpal got it to a place where it passes all tests.
I think we just need this now, again quoting @effulgentsia from #255:
Then this can be RTBC.
Comment #282
wim leersI've unpublished comments #272 through #281, to remove all the noise. @drpal, you said you were going to update the issue summary? :) Very much looking forward to that, because I can RTBC after that!
Comment #283
droplet commentedBefore @effulgentsia's cool comments.
My Evaluation:
the behavior changes of the last patch compare to GIT HEAD: (don't just look at the tests, that designed to get passed)
- No more single-root anime.
- And very odd anime for some cases. e.g., #269 example
Only applies to ELEMENT_NODE. All further callback involved the inserted content (non-ELEMENT_NODE) would be broken.
Since it chopped the callback content into small pieces. Unexpected behavior may happen (for example, developers think single insertion before and apply jQuery.once or Durpal Behaviors to that block, now they have to deal with all chopped blocks. It's very bad DX IMO.)
(I see this as a bug of the patch actually)
HARD
For the sites will run into the issue, you have to add an extra callback to modify the responses or change response from sources.
Few questions/suggestions:
- The main feature of this function
- limitations of the new insert
(everyone looking for a different little piece of this patch, I hope our reviewers think of each problem carefully.)
Comment #284
effulgentsia commentedRe #271, sorry I haven't updated the IS here yet. Before I do so, I want to clarify what I wrote in #255:
Here's a way to see that. Apply this interdiff on top of #270. If you do that, then if you enable the ajax_test module, navigate to
/ajax-test/insert-inline-wrapper, and click on "Link html mixed", you'll see the animation shown in theajax-patch.movfile attached to this comment.If you then revert the changes that this patch makes to ajax.js, clear caches, refresh the page, and click that link again, you'll see the animation shown in the
ajax-head.movfile attached to this comment.I think this odd behavior of the patch version is what @droplet is referring to in #283 as "No more single-root anime...very odd anime for some cases."
As I wrote in #255, I think that's ok though, or at least preferable to any other option we've explored, including leaving HEAD as-is. Modules that encounter an unexpected animation as a result can alter their responses to provide a wrapper. But I'd like to get a front-end framework manager to weigh in on that as well.
Regarding the other feedback in #283:
What's an example where that's a problem? What are possible behaviors that act on non-elements nodes?
What's an example of a behavior that would function differently when applied to each top-level element separately vs. to a wrapper element?
I think a #ajax setting to either opt-in or opt-out of an auto-generated wrapper is worth some consideration. For me, which one should be the default depends on whether it's only animations that are affected, or if behaviors are also affected, so I'd love some more insight on real or hypothetical behaviors that might be affected.
Yes, I think it probably is. We could probably remove most of the scenarios, but still leave key ones like the "mixed" ones.
how?
Comment #285
Anonymous (not verified) commentedGreat work here! Patch #270 works like a charm! It removes the disgusting
div, whose source is not entirely obvious (since it does not contain any identifying attributes).In the times of css flex, this is a very annoying wrapper, since the sensitivity to the nesting of the elements is again increased. Another important reason is the increased use of AJAX in 8.5.
As a simple development, I super plus the idea to never wrap any response. Or at least, make this behavior configurable (ui checkbox with disable wrapping by default).
I do not know how much important animated luster. While broken layout is a fatal problem. So, +1000 for open follow-up, if we have problem with animation after it, but fixed layout.
IMO, if the developer did not take care of the necessary wrapper, then finding a problem, he will only blame himself for it. While trying to figure out the reason undefined
wrapper, he is unlikely to be so self-critical.Comment #286
s.messaris commentedMany thanks for #270. I have used ajax extensively since D7 and have been applying dirty hacks to get around layout breaking because of the wrapping. I was a bit disheartened when first trying D8 and seeing this affected BigPipe as well. Since using #270 I have had no issues, I am very happy about that! Many thanks to everyone that has worked and is still working on this!
Comment #287
Leagnus commenteddrpal, thank you,
#270 resolves adjacent issue
Comment #288
khiminrm commentedPatch from #270 helps me to fix 'html' method in ajax for select element in custom form. Without patch I have div wrapper for value returned by ajax callback. Thanks!
Comment #289
wim leersCan we please get that issue summary update here, so that we can move this back to RTBC?
Comment #290
GrandmaGlassesRopeManUpdated.
Comment #291
wim leersComment #292
GrandmaGlassesRopeManComment #295
edurenye commentedThe patch needs a reroll.
Comment #296
edurenye commentedRerolled.
Comment #297
GrandmaGlassesRopeManAlright. I think this reroll looks good. 🎉™️
Comment #298
dillix commentedIs there a chance to get it fixed for 8.5.0?
Comment #299
wim leers#298: Yes, but it's at committer discretion. We should assume it won't make it into 8.5.x.
Comment #300
dillix commentedIt would be great if we will see this in 8.5.x after 8 years of patching core :)
Comment #302
wim leersTests only failed because HEAD is currently broken.
Comment #303
alexpottI think we need a change record to detail that the wrapping div is no longer going to be there. And whilst I agree it is not API someone might be targeting something using the
divin CSS. I don't think we should hold back from changing this because of that though.Comment #304
GrandmaGlassesRopeManComment #305
GrandmaGlassesRopeManComment #307
Anonymous (not verified) commentedComment #308
marcoscanoQueued #296 to be tested against 8.5.x, in the hope that it can be cherry-picked.
I have manually tested it with 8.5.x and it applies cleanly and works as expected in our project scenario.
Comment #309
itsekhmistro commentedManually applied patch from the comment #296 to 8.4.4 (there is difference in js variable names), it works well. No regression has been found so far.
Comment #310
spadxiii commentedThere is a little issue with this patch: inline svg does not work anymore in Chrome. Somehow it works fine in Firefox...
I traced it down to
var $newContent = $($.parseHTML(response.data, true));which seems to break the inline svg.Replacing this with a simple
var $newContent = $(response.data);seems to work, but I'm not sure why.In my case, I have a menu-block in the footer, where each link has an inline svg (with use). This works fine without bigpipe, but when bigpipe is enabled, the menu-block is added via js and the svg is not showing anymore.
I've added this comment in case others run into this problem. I'll leave this as RTBC as my issue might be more of an edge-case.
Comment #311
idebr commentedTo expand on the issue in #310: I have noticed this occurs when using svg markup that references an external file, for example:
The project I am working on includes the svg4everybody library. Enabling the 'polyfill' option that replaces this markup with the actual svg markup fixes the display issue.
Comment #312
GrandmaGlassesRopeMan@SpadXIII
The reason we need to use
$.parseHTML()in addition to wrapping the response as a jQuery object is due to leading or trailing text nodes. For example,Here is some more information on what is actually happening with $.parseHTML().
@idebr
I built a few test cases with an XML Document being returned and didn't notice anything strange happening. Perhaps there is some malformed XML being returned that causes the parser to fail?
Comment #313
dillix commentedWhat we need to get this committed?
Comment #314
alexpottI've tried to look at the #311
This outputs:
It would be great if @SpadXIII and @idebr could do some more debugging since things look sensible as far as I can see.
Comment #315
GrandmaGlassesRopeMan@alexpott
That's the behavior I observed. I tried inserting the HTML a bunch of different ways and didn't notice any strange behavior with inline XML.
Comment #316
idebr commented#314, #315 Sorry for the late comment.
I have attached a module 'svg_test' that displays the failing behavior:
- /svg-test/bigpipe renders an inline svg that references an external svg sprite.
- /svg-test/default is the control group. It uses default rendering.
Regarding your observations: Yes, the output is identical before and after the patch. However, after patch the icon is no longer rendered. I have only been able to reproduce this behavior in Chrome.
Attached screencast shows the behavior including dramatic mouse movements:
Comment #317
GrandmaGlassesRopeMan@idebr Would it be possible for you to post the module code some place, perhaps Github? I'm not totally thrilled about downloading a zip from a relatively unknown location.
Comment #318
GrandmaGlassesRopeMan@idebr
I tested the
svg_testpatch in Chrome 63 this morning. I wasn't able to replicate any strange behavior with the external svg loading.Comment #319
idebr commentedRe #318:
In your screencast I noticed you are not logged in. BigPipe only uses ajax for authenticated users, so you will notice no regression when testing with an anonymous user.
Comment #320
GrandmaGlassesRopeManughOk. So I see this when I'm actually on the correct branch. The side effect of working on too many things at once.
Comment #321
GrandmaGlassesRopeManAlright. Marking this as Needs work. 😭
There's a couple of issues here, I'll do my best to highlight what is happening. When we call
$()on the result of$.parseHTML(response.data, true);, this uses the browsersinnerHTMLmethod. SinceinnerHTMLis only available onHTMLElement, notSVGElementwe can't use it to parse XML-like elements.There is an experimental technology, DOMParser, which would allow us to parse an XML document. However that opens a door to inspecting every string returned for XML-like nodes and parsing them with a different parser.
If you want to create a new a new SVG element, you can do something like,
This uses the native browser API's, and additionally sets the namespace of the created element.
There is however a workaround to all of this...
One additional thing to mention, this fails in Chrome and Safari, but not Firefox. There's probably some additional investigation there. Some browsers appear to be implementing the spec correctly, while others do not.
Comment #322
GrandmaGlassesRopeManAlright. I don't know if this is a good idea. This is probably a bit too brittle, but does solve the exact issue with AJAX insertion of SVG data.
Comment #323
GrandmaGlassesRopeManLet's hold off on this until #2946522: Provide a mechanism to get a random string, suitable for element IDs is done.
Comment #324
piyuesh23 commentedNeeded to use this on an ongoing project running on 8.4.5 & re-rolled the patch in comment #119. Attaching it here that it helps anyone else seeking the patch.
Comment #325
andypostbtw Anyone used to test inline JS?
Comment #326
marcoscanoDo we really want to depend on that? The new method will likely only be included in 8.6, and this is a very old bug affecting many projects, so perhaps the benefit of committing it now would outcome the drawback of not using the new method?
Maybe we can try to get this in as is, with a @todo to make the change once #2946522: Provide a mechanism to get a random string, suitable for element IDs lands?
Comment #327
dillix commented@drpal, may be we should commit this issue as is and create follow up for full svg support?
@piyuesh23 can you re-roll patch for 8.5?
Comment #328
andreyks commentedComment #330
GrandmaGlassesRopeMan@andreyks Can you post an interdiff? It will really help with figuring out what changed.
Comment #331
Anonymous (not verified) commentedAdditional negatives from this unexpected
div:File upload/remove:

Layout rearrange:

Field UI:

#322 still works fine!
Comment #332
Anonymous (not verified) commentedNR for #322. RTBC++
Comment #333
Anonymous (not verified) commented#325: if it is a question about
html tagsinside of<script>, then this is a separate issue #2822525: FilterHtml leaves unclosed tags inside inline scripts.#330: attached interdiff-322-328, but I did not understand why this was done :)
#333 review:
Add test coverage for svg (PhantomJS failed, so Selenium).
Improve tests (i hope). Because
$assert->responseContains($expected);does not works for 'empty' case in last version of mink. It seems this is another regression in addition to #2948700: Casting $text value to string in responseContains/responseNotContains methods. But it's good, because testing a''on a page is pointless.So additional wrapper is added, and the 'contains' assert is replaced by 'same' assert. Due to this, there is no need to assert
responseNotContainsanymore.Fixed few nit doc flaws.
Comment #334
lokapujyaWhy is this code in the comment?
Comment #335
Anonymous (not verified) commented#334: good question, reverted.
Comment #336
lauriiiI changed the svg handling into a more elegant and simple solution which should fix the bug. Few weeks ago I tried to write a test case for this but I couldn't figure out how to do that since the bug was caused by browser rendering bug.
We should still try to address the change in effect behavior when passing multiple root elements. I proposed that we would add a wrapping div and give a deprecation warning. However, later I realized that we don't have a way to set deprecation messages in JavaScript.
Comment #338
Anonymous (not verified) commented#336: WOW! I'm not very good at this part of the patch, but it looks really elegant!
The only reason the test failed was the
'processed'class appeared:I hope we can easily live with this extra class, so just updated the expected value.
Also replaced the model
2 test + dataProviderto1 test + 2 asserts. Much faster testing now:Comment #339
Anonymous (not verified) commentedOpps! I was so happy that I lost one of the test cases in a hurry. Restored.
Comment #340
lauriiiComment #341
Anonymous (not verified) commentedComment #342
lokapujyavery nitty:
let's be consistent and use single quote here.
Comment #343
Anonymous (not verified) commented\nnot works inside of single quote. So double quote here.Comment #344
lauriiiComment #345
dinesh18 commented#339 looks good to me.
I agree with comment #343. +1 to RTBC
Comment #346
zenimagine commentedIt is many that I do not understand why there is a div around some view. I finally found the right question
Comment #347
lokapujyaRemove the remaining task. Put the answer in the issue summary.
Comment #348
lokapujyaI'm confused. If it is inserted wrapped, why is the response not wrapped. Does this need to be clarified or am I just not understanding the comment correctly?
Comment #349
lauriii#347: I don't think we've made a decision how are we handling that BC break and that's why it's in the remaining tasks.
#348: Good catch, I think we need to update the comment.
Comment #350
Anonymous (not verified) commented+1 to make this decision in follow-up issue :)
Animation problem is negligible compared to the broken pages on the site (css rules and js scripts can be based on page layout).
But if not, what about decision from #336 with wrapper from Drupal.theme (attached file).
Comment #351
lokapujyaOpened the followup: #2969658: ajax.js insert command: Decide what to do with the effect behavior change if there's multiple root elements.
Comment #352
Anonymous (not verified) commented#351: Thank you, @lokapujya! +1
Also after a series of experiments I was convinced that we really should not by default wrap multi-root-elements.
Because any space, any html comment makes the response as multi-root-elements. This is extremely inconvenient.
It is much better to make an empty wrapper by default, and CR with instructions like:
A patch is created based on the proposed idea + extended test for the custom js-wrapper.
Comment #353
skaught#352 -- this should be in the change record:
Comment #354
Anonymous (not verified) commented@SKAUGHT, thanks for quick feedback! Yes, if we all (and especially the mantainers) agree with this way, then I will update the change records (although I will be grateful in the help of those who speak English better than I, that is from anyone 😉)
I also want to draw attention to the fact that @effulgentsia made the demonstration of "odd" effect in #284 in the slow motion (for better study). In reality, this is a fairly fast process. >300ms animation is annoying, I remember that it is a bad design practice for usual cases.
Comment #355
lauriiiThe multi-root ajax responses are only a problem if there's an effect. How about we only add the wrapping div if there's multiple elements on the root level, and there's an effect?
Comment #356
Anonymous (not verified) commentedFor example, next multiple cases haven't problem with effect:
<div>Response data</div><!-- start response data --><div>Response data</div><!-- end response data -->Therefore, the wrapper will not be of use to them.
Can it be better to just disable the effect for multi-roots? I think not. Odd animation effect looks not so bad for me.
In #2969658: ajax.js insert command: Decide what to do with the effect behavior change if there's multiple root elements. we can try to improve next part:
Example, add special
animation-delayviaeffectto each tag-elements from multiple-root (because we know count elements, and effect speed).But it seems to me, that we are just trying to help make a good animation for an incorrectly wrapped data. I am not against that, but is this the reason for the delay? Maybe it's better to leave it for the follow-up. Perhaps when more developers start works without this 'div', we will get more examples of animation problem, and we will be able to create the best solution.
Comment #357
lauriiiThis is important because there are existing sites that rely on this behavior and for that reason we should try to avoid changing it. It is not so much about does it look bad or not, but about the fact that someones site might break because of this. For that reason we also cannot defer fixing that in a follow-up, unless we decide to do a breaking change.
The support for effects with multiple root elements is meant to be temporary. I also proposed that we give a warning for any users that would be affected by this logic, but we don't have a way to do that until #2962344: [policy, no patch] Document how to handle deprecations in JavaScript has been resolve.
So if we only add the div when there's multiple root elements and an effect, the bug would be fixed in most cases, and there wouldn't be any known breaking changes for existing sites.
Comment #358
Anonymous (not verified) commentedThanks for clarifying. It makes sense :(
Done via additional theming method
ajaxWrapperNewContent.Because we can not guarantee that this
divis needed only for official drupal js-effects (it can be used in css or in custom js).I also added special filter to exclude from the counting such elements as
'#comment'and'#text'that contains only whitespace chars.But if a developer wants to wrap any type of multi-root element, he can just do next:
If a developer wants to custom wrapper only for default multiple root logic:
Also now the test coverage should check:
All checks should be for inline/block pages.
All checks should be for insert/replace method.
I adjusted the tests for this, but the check about 'theming full response wrapper' out of the loop, because it is already too big :)
Comment #359
Anonymous (not verified) commentedI also doubt that it will be correct to deprecated multiple root elements response. If I want to return a set of cards, and I want them to slide from one point, then I just need that "oddly" animation effect, and multiple root elements.
At the moment, Drupal is wrapping everything multi root elements, and we want to keep it for BC. But in fact it limits the use cases. With two new methods from #358 developers will finally have the opportunity to break these fetters.
Comment #360
GrandmaGlassesRopeManUse const here.
typeEffectis never reassigned.This is a bit awkward. It's generally discouraged to use a
for...inloop now. Since$newContentis a jQuery element you can probably use .filter() to reduce the array of elements.Comment #361
Anonymous (not verified) commented@drpal, thanks for hints! Done.
Comment #362
lokapujyashould be
We can clean up the comments in a followup. For example, "not wrapped" maybe could be "unwrapped". Or, "Test that not wrapped response data (text node) is inserted wrapped" could maybe just be "Test that text is inserted wrapped". But the comments should at least say the correct action before RTBC.
Comment #363
GrandmaGlassesRopeManSorry I didn't catch some of this before.
The sum of this function is to either return
$newContentunmodified, or wrap$newContentif some conditions are met. I think we can reduce the complexity just a bit,Arrow function.
You can use the implied return syntax here.
Comment #364
Anonymous (not verified) commented#362: Sure, done.
#363: Wow, looks nice, thanks! Done.
Also few other fixes in php-test, the main of which:
Because of this flaw, the test did not check all cases, and as result some code was wrong.
Also move
$custom_wrapper_multiple_rootscript out of loop too, because now the defaultajaxWrapperMultipleRootElementswrapper checks the processing of multi-roots, and for the custom it will be enough just to check one case.Comment #365
dillix commentedLooks good, plus for RTBC.
Comment #366
lauriiiThanks everyone for the fantastic work! 🏋️♂️
This comment should be updated.
We should go through the test cases and the documentation and make sure all of them are up to date. It seems like that there's multiple comments that are out of date.
We should document that these are deprecated and will be removed in Drupal 9.0.0. We should also open a follow-up for triggering a deprecation warning here.
Should we rename the variable to something like
$expect_wrapper_cases? We could also add a comment here that this is temporary behavior.Comment #367
lauriiiUpdating the issue summary with information about the BC layer for the multi root element inserts with effects.
Comment #368
Anonymous (not verified) commented#366.3: If I understand correctly, then we should not make a behavior change between major releases of Drupal anymore. That is, we can not use wrapper in D8, and stop doing it in D9. This should happen between minor releases.
Should we mark both theming methods like deprecated? Looks like not. They provide an opportunity to theming response data.
The problem is only in one line:
$('<div class="wrapper-ajax-multiple-root-elements"></div>').append($elements)Because by default Drupal should not decide whether to wrap response data.
So, do I correctly imagine a further plan:
Step 1. Now:
Step 2. After the implementation of the trigger (example in 8.5.x or in early 8.6.x):
Step 3. During the full minor release we show the deprecated message (if the user does not override it and has related cases). And in the next minor release:
We will not remove the ability to theming response, just do not wrap it by default. And the user can always easily get the previous behavior.
Drupal.theme.ajaxWrapperNewContent = ($newContent, ajax, response) => (This method generally remains unchanged, it allows to theming even more globally. For example, add a wrapper to any response regardless of the effect or multi-root case.
Comment #369
lauriiiWe want to avoid making changes that are likely to break existing code. I think the current logic is fine; it removes the div unless there is multiple root level elements and an effect attached to the ajax event.
This is only temporary. We deprecate those theme functions to educate people that they will be removed in Drupal 9.0.0 since we don't want to add the wrapping div in any use case.
I'd say the step 3 should be removing the theme functions fully from Drupal 9.0.0. They are only there temporarily added to allow people remove the div if they are confident that it doesn't affect their site.
Comment #370
Anonymous (not verified) commentedThanks for this additional explanation!
#366.1: Done (i hope)
#366.2: Not found problem in AjaxFormPageCacheTest. But big rewrite AjaxTest :) Because it looks that it will be better for reading and support. Sorry if not.
#366.3: Done (#2973400: Mark functions for wrapping Ajax response as deprecated)
#366.4: Done above
$render_multiple_root_wrapper.Reveiw "big rewrite AjaxTest":
Just decouple on two groups. Now it is the same content (easy to copy/paste).
We everywhere check block/inline insert support, so combine in one assert.
If we clear content from 'processed' classes, we get rid of the need to manually set it for expected values. What simplifies the test for wraps problem.
Comment #372
Anonymous (not verified) commented#370: double interdiff, sorry!
Comment #373
lokapujyawhy remove the test comments? those were basically the only documentation. Edit: Although looking closely, they are kind of redundant; The class names are somewhat descriptive of the test case.
Comment #374
lokapujyaAre there any contrib modules that will test against this once it goes in?
Comment #375
Anonymous (not verified) commentedI updated change record. Still tag 'Needs change record updates' for review.
#373: Yes, test cases have keys with good names, and they are grouped together, and now completely coincide with data from
AjaxTestController. Thus, comments are not of much more value. I hope for support from the author of these comments, because I know his preference for Spartan brevity in such things as code.Comment #376
lokapujyaIn the the change record, it's not clear that we are only NOT removing the div ONLY when both a.) for multiple root elements and b.) when we have an ajax affect. It is clear in the issue summary.
Comment #377
Anonymous (not verified) commented@lokapujya, thanks for review! CR updated per #376.
Comment #378
lokapujyaA patch was RTBC'd months ago. More improvements were done and now it needs some real world testing.
Comment #380
lauriiiIt seems like we are close to 🚀here!
We could mark these deprecated already, we just cannot trigger an error.
This comment should be updated since we are not parsing anymore.
Can we add a comment here why are are calling this theme function?
👍
Comment #381
droplet commentedSomeone calling me. I scanned few comments quickly and would like to point out something really quick on SVG
#296 - latest patch without SVG patching.
#312 @drpal explained few reasons that why to use
$.parseHTML.But, in jQuery 3.x+,
$.parseHTMLhas a hidden feature to stop scripts being executed (during parsing). It usingdocument.implementationinternal rather than executed in the same document context.You can checkout the source code here:
https://github.com/jquery/jquery/blob/2d4f53416e5f74fa98e0c1d66b6f3c285a...
And above code is the currently planned workaround for SVG.
So what's the difference between jQuery 3.x
$.parseHTMLand above$.htmland jQuery 2.x$.parseHTML.Yeah. You Got it!. the execution scope! Then, we could passing `document` into it:
In advanced usage, we could grep the content to decide if to pass the `document` scope or not.
Cheers.
Comment #382
Anonymous (not verified) commented#378: thank you, @lokapujya!
#380: thank you @lauriii! All points done except #2 (because #381).
#381: a pleasant surprise to see the post from @droplet again! I was so excited that I did not understand anything, but I will gladly accept your version!) Especially since it makes again relevant the comment:
// Parse response.data into an element collection.And we do not need to think about a new comment :)
Comment #384
Anonymous (not verified) commentedComment #385
lauriii@droplet thank you very much for the comment! 😊
The problem indeed is that
parseHtml()is usingdocument.implementation.createHTMLDocument()as context on browsers that support it. Using that as context will prevent browser from loading assets for the parsed markup, which as a result breaks the SVGs with dependencies and probably some other things as well. So as a solution we force it to use document as context always and then we can safely useparseHtml()in our use case. 🎉@vaplas thank you for your long-term commitment to fixing this issue! 🚣♂️
There's few more things related to documentation I would like to get sorted before marking this as RTBC:
Let's provide a bit more documentation about when we are adding the wrapper. 🐾
This should be 8.6.x since I don't think we will be able to backport this into 8.5.x. 👮♂️
We should create a separate change record for this with instructions how to avoid break after this BC layer gets removed. Once we've done that, add the link here for more background information. 📝
Nit pick: we don't want to trigger an error but a deprecation warning 🕵️♂️
It would be probably good to mention one more time that this will be removed before Drupal 9.0.0 🧐
Comment #386
Anonymous (not verified) commented@lauriii thank you for your long-term helps with it too!
385.1: Done (i hope)
385.2: Done.
385.3: Done. https://www.drupal.org/node/2974880
386.4: Done.
385.5: Done (i hope)
+ Nit clean up in AjaxTestController.
Comment #387
lauriiiSome minor improvements to the documentation. I also removed the class from the div added as wrapper. Other than that looks good for me!
Comment #388
webchickI spoke to @lauriii about this, and he said that given the trickiness here, it could use an additional framework manager review. Tagging for that.
Comment #390
dillix commentedComment #391
alexpottRe-rolled and fixed a code style issue.
Comment #392
lauriiiSaving the issue credit
Comment #394
GrandmaGlassesRopeMan- fixes failures from #391.
Comment #395
justafish👌
Comment #397
lauriiiThank you everyone for the amazing work on this issue. The most recent change has been reviewed by multiple subsystem maintainers and droplet. We've also provided a BC layer for the animation problem that has been a long standing concern on the issue.
Committed 5f0f49d and pushed to 8.6.x. 🚀 Thanks!
Comment #398
lauriiiOops, I didn't mean to tag this as a v6.x-1.12 blocker.
Comment #399
lauriii⬅️✌️➡️
Comment #400
dmsmidtAwesome! Good job getting this over the finish line after so long!
Comment #401
dillix commentedWill this be backported to D7?
Comment #402
andypostD7 needs separate issue
Comment #403
nod_Great work, thanks a lot for getting this one done!
Comment #404
jibranWhen did we stop backporting bug fixes(major in this case) to 8.5.x? I know in some cases we don't backport bugs but can core committer please state the reason of not backporting in the commit comment? Thanks!
Comment #405
abacaba commentedGreat! Thank you for your work! :)
Comment #406
lauriii@jibran bug fixes where the benefits out-weights the potential risk of disruption are generally backported to the previous minor branch to be included in the next patch release. I decided not to backport this issue to 8.5.x. because of the risks involved with the markup change. I will pay more attention in future to include the reasons behind the decision to not backport certain issues.
Comment #407
jibranThanks, for explaining that @lauriii. I amended the wording at https://www.drupal.org/core/backport-policy https://www.drupal.org/node/767608/revisions/view/10349292/11024653
Comment #408
wim leers💦💦💦💦💦💦
(That's the sweat that was poured into this issue.)
🎉🎉🎉🎉🎉🎉
(That's the celebration for seeing this finally fixed, after nearly 8.5 years.)
Having this fixed also fixed #2738685: BigPipe leaves wrapper <div>s in place, because AJAX system leaves them — this can cause some CSS to break (the only BigPipe bug!) and #2913563: Facets blocks loaded with BigPipe wrapped in unwanted <div> due to bug in AJAX system (Facets was affected by the BigPipe bug which really was this bug.)
Comment #410
dave reidSo one thing to be aware of for this, is that with multi-element AJAX inserts, those elements are now root-level elements when the Drupal JS behavior attachments are run. When you use
$('.my-element', context);as the selector and .my-element is a root-element loaded via AJAX, this element can no longer be found. This caused a regression in the Token module: #2994671: Token modal unusable with Drupal 8.6. I don't think this behavior of jQuery selectors was considered or mentioned in the change record. I would anticipate more modules that load contents via AJAX and then process the content via JS behavior attach to have regressions.Comment #411
effulgentsia commentedI opened #3001570: Drupal.attachBehaviors() documents inconsistent instructions about the context parameter for #410.