Skip to content

Issue #17045: UI Flickering on Checkstyle.org – Panel Resizes Erratically on Navigation#18036

Merged
romani merged 1 commit into
checkstyle:masterfrom
stoyanK7:17045
Nov 11, 2025
Merged

Issue #17045: UI Flickering on Checkstyle.org – Panel Resizes Erratically on Navigation#18036
romani merged 1 commit into
checkstyle:masterfrom
stoyanK7:17045

Conversation

@stoyanK7

@stoyanK7 stoyanK7 commented Nov 1, 2025

Copy link
Copy Markdown
Collaborator

Issue

Summary - What changed?

  • Changed the event that triggers setBodyColumnMargin() from window ("load") to document ("DOMContentLoaded")
    • The load event fires only after the whole page has loaded - scripts, images, iframes, stylesheets,... 1. We could instead use the DOMContentLoaded event that fires earlier - when the HTML document has been completely parsed, and all deferred scripts have downloaded and executed2. Even the docs suggest that using load is a common mistake2:

      A different event, load, should be used only to detect a fully-loaded page. It is a common mistake to use load where DOMContentLoaded would be more appropriate.

      We don't need a fully loaded page to set the className of #leftColumn and #bodyColumn, just the DOM. Therefore, using DOMContentLoaded is something we can do.

  • Added async and defer attributes to the checkstyle.js script so that it's fetched in parallel to the HTML parser
    • Until now, that was done in sequence
    • The official HTML specification3 has a very good diagram explaining what each attribute does:
      • image
      • They also say that

        "The defer attribute may be specified even if the async attribute is specified, to cause legacy web browsers that only support defer (and not async) to fall back to the defer behavior instead of the blocking behavior that is the default."

Does this all work?

I see an improvement in 80% of cases. Though sometimes flickering still occurs (for instance, on the Release Notes page). You just have to test on your browser.

Footnotes

  1. Window: load event - Web APIs | MDN

  2. Document: DOMContentLoaded event - Web APIs | MDN 2

  3. HTML Standard 4.12.1 The script element

@stoyanK7

stoyanK7 commented Nov 1, 2025

Copy link
Copy Markdown
Collaborator Author

Github, generate website

Comment thread src/site/site.xml
Comment on lines +54 to 57
<script type="text/javascript" src="$relativePath/js/checkstyle.js" defer async></script>
<script type="text/javascript" src="$relativePath/js/anchors.js"></script>
<script type="text/javascript" src="$relativePath/js/google-analytics.js"></script>
<script type="text/javascript" src="$relativePath/js/copy-clipboard.js"></script>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, we should add defer async to the rest of the scripts too. I see no reason not to.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adding defer I can understand but adding async will make the script execute in parallel with HTML parsing that means while the scripts are executing, the DOM might not have been parsed and we need the DOM to be parsed before executing the script. Please correct me if I am wrong.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adding defer I can understand but adding async will make the script execute in parallel with HTML parsing that means while the scripts are executing, the DOM might not have been parsed and we need the DOM to be parsed before executing the script. Please correct me if I am wrong.

+1, I too think only defer is fine, async is not required it might cause conflict

@stoyanK7 stoyanK7 Nov 2, 2025

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, the script will execute before the DOM is parsed.

However, the execution of the script only registers the listeners (.addEventListener()). The callback function that is registered with each listener will trigger only when the corresponding event fires.

If we take the following code:

window.addEventListener("load", function () {
"use strict";
scrollButton = document.querySelector(".pull-right > a");
scrollButton.innerText = "To Top";
scrollButton.style.display = "none";
scrollButton.addEventListener("click", function (event) {
event.preventDefault();
document.documentElement.scrollTop = 0;
});
});

The listener will be registered before the HTML parser is done, but the callback (everything within function () { ..} will run only when the "load" event fires. And the "load" event fires way after the DOM is parsed. Same applies for the DOMContentLoaded event.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the clarification!

@stoyanK7 stoyanK7 Nov 2, 2025

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we do that in a separate issue? I'll open it if you are okay with that. Just so this PR and issue remain scoped to the UI flickering issue.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's merge this PR, and extend all others scripts in separate PR

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created an issue for the other scripts:

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Zopsss Please let me know if there's anything else to do.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay. Everything looks fine. Thanks a lot for detailed explanations!

@github-actions

github-actions Bot commented Nov 1, 2025

Copy link
Copy Markdown
Contributor

@stoyanK7

stoyanK7 commented Nov 1, 2025

Copy link
Copy Markdown
Collaborator Author

Here is a video comparing https://checkstyle.sourceforge.io and the generated website in the above comment:
Screencast from 2025-11-01 15-59-45.webm

Firefox on Ubuntu 24.04

@stoyanK7 stoyanK7 marked this pull request as ready for review November 1, 2025 15:01

@romani romani left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tested in Chrome. With this update works better.

most of the time, I open this website from phone, and phone has different rendering and this problem is not visiable.

thanks a lot!

@romani

romani commented Nov 1, 2025

Copy link
Copy Markdown
Member

@smita1078 , please looks at this change too.

@stoyanK7

stoyanK7 commented Nov 3, 2025

Copy link
Copy Markdown
Collaborator Author

Converting this to a draft. There is a race condition I did not take into account.

@stoyanK7 stoyanK7 marked this pull request as draft November 3, 2025 19:29
Comment on lines +161 to +167
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", setBodyColumnMargin);
}
else {
setBodyColumnMargin();
}

@stoyanK7 stoyanK7 Nov 3, 2025

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This if-else is mandatory to prevent a race condition where the DOMContentLoaded event might fire before the listener is created. Taken straight out of the docs1:

Sometimes your script may run after the DOMContentLoaded event has already fired. This typically happens when the script runs asynchronously. Common scenarios include: [...]

  • A script that is included via <script async>.

[...] In these cases, you should check the document's readyState before adding a DOMContentLoaded listener, or your setup logic may not execute at all. [...]
Consider the following script file in isolation:

function doSomething() {
  console.info("DOM loaded");
}

if (document.readyState === "loading") {
  // Loading hasn't finished yet
  document.addEventListener("DOMContentLoaded", doSomething);
} else {
  // `DOMContentLoaded` has already fired
  doSomething();
}

The script can't enforce how it's included by the HTML. If it's included via <script async>, or it's dynamically injected, then by the time it executes, DOMContentLoaded has already fired. To ensure that doSomething() always runs when the script loads, we need to have two paths, one that immediately runs doSomething if the document is already loaded, and another that runs doSomething once the document is loaded.


This race condition can happen with the DOMContentLoaded event, but not with load2:

The load event is fired when the whole page has loaded, including all dependent resources such as stylesheets, scripts (including async, deferred, and module scripts), iframes,

Footnotes

  1. Document: DOMContentLoaded event - Web APIs | MDN

  2. Window: load event - Web APIs | MDN

@stoyanK7

stoyanK7 commented Nov 3, 2025

Copy link
Copy Markdown
Collaborator Author

Github, generate website

@stoyanK7 stoyanK7 marked this pull request as ready for review November 3, 2025 20:06
@github-actions

github-actions Bot commented Nov 3, 2025

Copy link
Copy Markdown
Contributor

@romani

romani commented Nov 9, 2025

Copy link
Copy Markdown
Member

@smita1078 , and see comment from you, please finish review

@romani

romani commented Nov 11, 2025

Copy link
Copy Markdown
Member

Ok , looks like @smita1078 is busy with other activities, we moving forward, we can always improve in separate PRs

@romani romani merged commit bfd8fea into checkstyle:master Nov 11, 2025
121 checks passed
@smita1078

Copy link
Copy Markdown
Contributor

Ok , looks like @smita1078 is busy with other activities, we moving forward, we can always improve in separate PRs

Sorry, missed it!

@smita1078 , and see comment from you, please finish review

Yes, I went through the discussion — the race condition that @stoyanK7 mentioned aligns with the concern I had earlier regarding potential conflicts when using async. I see that it’s already been taken into account in the latest changes. All good from my side 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants