-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Avoid unnecessary structure updates in renderer #4347
Comments
Typing inside empty boldTo understand what's exactly happening during rendering and how renderer can be improved (so this case is analyzed from Starting with HTML like:
Typing
Of course there is much more happening during typing, but this steps are important for renderer. To sum up, transformations performed on the real DOM by the renderer:
In this particular case |
Typing on the end of bolded linkI took a look also on case described in https://github.com/ckeditor/ckeditor5-engine/issues/1342#issuecomment-373778804, but now it seems there is only a problem with bold rendering (same as described above) and:
no longer occurs. Looks like it was affected by some changes along the way. The But I found similar behaviour when typing on the end of bolded link:
This case is different, due to the fact that browser natively changes DOM structure. Here I'm not sure if it would be possible to somehow simplify rendering cycles. The situation here probably could be improved by implementing inline filler improvement mentioned in #4303 (Fillers section, point 1). The fact that whole I also checked Firefox and there it works perfectly. DOM structure is not modified, typed character is inserted on the end of bolded link so |
Bolding selected textAnother interesting case is bolding part of the text. Starting with HTML like:
and pressing ctrl/cmd + b (or
All 2 - 4 steps are happening inside The In such case reinserting whole text will break the composition. So the reasonable way would be inserting only Extracted to ckeditor/ckeditor5-engine#1425. |
To sum up, as also previously mentioned by @Reinmar (in #4303), it looks like the
which will then require updating mappings:
I have some doubts if above will be enough, but it is a good starting point. Such approach should solve most cases similar to the first one (https://github.com/ckeditor/ckeditor5-engine/issues/1417#issuecomment-388809745) described above, when elements are unnecessary replaced even though only its children have changed. Important thing to keep in mind is that such cases (that new element is already in the DOM) are usually triggered by external actions (like typing). All changes which first appear in Cases similar to the third one (https://github.com/ckeditor/ckeditor5-engine/issues/1417#issuecomment-389191257) are a little different. Here rendering is needed (as change is triggered internally), but optimizing the number of elements replaced by the
At first glance it looks like it may not be solved by the general solution implemented for this issue (mainly because of mixed element / partial text updates) so alternatively we may extract it to a separate issue and handle it after this one. For the cases similar to second one (https://github.com/ckeditor/ckeditor5-engine/issues/1417#issuecomment-389155640 - DOM modified by the browser before rendering), which are more rare and occurs not in all browsers, number of DOM modifications could be optmized. It should be covered (at least partially) by the general approach solution. |
One more thing which comes to my mind is to check if order of rerendering elements makes a difference. As For the two first cases (3rd one has only one marked element so it is skipped): Typing inside empty bold
Typing on the end of bolded link
Two first orders have exactly the same set of additions/removals, however when outermost element (here I think for cases which needs rendering, we should consider sorting elements as it may decrease the number of operations performed on DOM structure. |
I basically built a PoC based on the above assumptions containing two main mechanism:
This works fine for typing inside empty bold (https://github.com/ckeditor/ckeditor5-engine/issues/1417#issuecomment-388809745) or when the whole view structure is replaced with the same structure (mappings are refreshed properly and DOM stays untouched). However, there is one case which makes this approach invalid. Consider HTML like:
Now, second paragraph (
Previously I see two solutions with which we may tweak the current approach:
TBH, I'm not sure if and how it can be done in a reasonable way since adding more and more logic to
We would like to prevent rerendering of elements which does not have to be rerendered. Such situation may only occur if |
We have decided to go with There are some cases in which it is not possible to reuse some existing DOM elements without moving them based on current
in short the
where Now
Diffing actual and expected DOM will result in the following set of operation: operation -> actual DOM structure
Step 3 is the problematic one here. The structure is For the first solution I suggest skipping such cases (and then see how and if they cause any issues and fix accordingly). This basically means that |
TL;DR The proposed solution refreshes There are a few ideas and reasons behind the way this solution was implemented, most of them already described in this issue, but I wanted to sum it up. As mentioned in the previous comment we decided to try with post processing the
sorting
|
# | Actual DOM | Expected DOM | diff() result |
postprocessed expected result |
---|---|---|---|---|
1 | 1<b>2</b> |
3<B>4</B> |
insert, insert, delete, delete | insert, delete, replace |
2 | 1<b>2</b> |
<B>4</B>3 |
insert, insert, delete, delete | delete, replace, insert |
3 | <b>2</b>1 |
<B>4</B>3 |
insert, insert, delete, delete | replace, insert, delete |
4 | <b>2</b>1 |
3<B>4</B> |
insert, insert, delete, delete | insert, replace, delete |
In the Actual DOM and Expected DOM columns above <b>
and <B>
means that it is a different element instance. Actions in bold (in 4th and 5th columns) are those which were identified as the ones replacing the <b>
element with a similar one (same tag name so it should be reused instead).
The naive approach performs correctly for cases like 1st one (from the table above) where postprocessed expected result and initial diff()
result has the same order of insert
/delete
actions (apart from the ones replaced by the replace action):
insert, insert, delete, delete
will be transformed into:
insert, delete, replace
However, if postprocessed expected result and initial diff()
result order of actions is different like in 2nd case from the table above where:
insert, insert, delete, delete
should be transformed into:
delete, replace, insert
the transformation cannot be simply done by iteratively checking all insert/delete
pairs for a matching element and requires running a full diff algorithm. So the post-processing runs the diff()
function which compares parts of expected and actual DOM corresponding to a given insert/delete
group. Elements with the same tag name (for example <b>
and <B>
from the table above) are treated by the diff()
function as replaced ones, resulting in correct expected result, same as shown in table above.
So I read it and tried to understand as much as I could on the first run :P. I think it is promising that you ended up with a double-diffing solution. I had similar kind of a problem in At the end, we have now a different solution in |
TBH, I don't understand the last 3 paragraphs of your comment. I guess you're comparing two approaches but you haven't clearly stated that and how does the first one fails exactly. Could you extend that bit because I feel someone may need to get back to this one day. |
@Reinmar I modified the description you mentioned, trying to describe more precisely which cases doesn't work, I hope it is easier to understand now. |
Fix: Renderer should avoid doing unnecessary DOM structure changes. Ensuring that the DOM gets updated less frequently fixes many issues with text composition. Closes #1417. Closes #1409. Closes #1349. Closes #1334. Closes #898. Closes ckeditor/ckeditor5-typing#129. Closes ckeditor/ckeditor5-typing#89. Closes #1427.
Extracted from #4303:
Also intersting analysis of a specific case is available in https://github.com/ckeditor/ckeditor5-engine/issues/1342#issuecomment-373778804.
The text was updated successfully, but these errors were encountered: