Skip to content

Add a jQuery-free vanilla JS implementation of wicket-ajax.js - #1580

Open
reiern70 wants to merge 3 commits into
masterfrom
reiern70/plain-js
Open

Add a jQuery-free vanilla JS implementation of wicket-ajax.js#1580
reiern70 wants to merge 3 commits into
masterfrom
reiern70/plain-js

Conversation

@reiern70

@reiern70 reiern70 commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Wicket's client-side Ajax/event support (Wicket.Ajax, Wicket.Event, Wicket.DOM, Wicket.Form, Wicket.Head, Wicket.Focus, ...) has always required jQuery. This adds a second, plain-JavaScript implementation exposing the exact same API, so applications that don't want to ship jQuery at all can opt in via:

getJavaScriptLibrarySettings().setWicketAjaxReference(WicketAjaxVanillaResourceReference.get());

jQuery stays the default; existing applications are unaffected.

Core changes:

  • wicket-ajax-vanilla.js: full port of wicket-ajax-jquery.js onto native XMLHttpRequest/DOM/event APIs (WicketAjaxVanillaResourceReference.java is its resource reference).
  • Removed jQuery from every other framework JS file that only used it incidentally (not as its actual implementation choice), so the whole stack can run without jQuery once the vanilla engine is selected: the extensions Ajax components (autocomplete, palette, upload progress bar, ajax download, trap focus), the dev debug bar, native WebSockets support, and FileUploadToResourceField.js - all verified to still work correctly under the default jQuery engine too, and their public extension points/callback signatures are unchanged.
  • Cleaned up several now-dead legacy-browser fallbacks and deprecated Web APIs encountered along the way (window.escape/unescape, window.event, Attr.specified, IE-only branches).

Testing:

  • Ported the wicket-core QUnit suite (channels/dom/event/form/head/ajax/timer) off jQuery-based test-authoring helpers so the same tests run against either engine; all.html now takes ?vanilla alongside the existing ?, and the Gruntfile has a matching qunit:vanilla target.
  • Ran the full suite for real (jQuery 3.7.1, jQuery 4.0.0, vanilla) via a portable Node build + jsdom against a real HTTP server, since no JS runtime was available here otherwise - this caught and fixed several real bugs in the vanilla engine (event "extra data" colliding with native Event.detail, beforeSend's settings.url/data not reflecting the resolved request, unsupported dataType 'json', a DOM.show() default-display mismatch, and scripts in replaced/appended markup never executing).

Examples:

  • wicket-examples can now be started with either engine via a single JVM-wide switch (-Dwicket.examples.use=JQUERY|VANILLA, default JQUERY), applied through AjaxEngineSelector from every example Application's init(), with a startup console banner (mirroring Wicket's own dev-mode warning) confirming which engine is active.
  • The shared example page header also shows the active engine and lets a visitor switch it for their session only, live, without an application restart - implemented via an IHeaderResponseDecorator rather than mutating the shared JavaScriptLibrarySettings, so concurrent sessions with different choices never interfere with each other. Verified end-to-end with WicketTester (both switch directions render the correct script tags in the correct order, and the toggle link updates the session as expected).

Documentation: new user guide section (ajax_11.adoc) explaining both implementations and how to switch; corrected IAjaxCallListener/ajax_5/ajax_6 docs that assumed jqXHR/jqEvent are always jQuery-specific objects.

.add(CSPDirective.STYLE_SRC,
"https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css")
.add(CSPDirective.FONT_SRC, "https://maxcdn.bootstrapcdn.com");
getCspSettings().blocking().disabled();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@papegaaij I would appreciate your help here: with strict the new VANILA js is no loaded. I could not figure out why

@reiern70

reiern70 commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

I will do some more testing but this seems to be working. Next steps

  1. Imporve examples to have more realistic APP
  2. Create selenium tests and infra to run them

@papegaaij

Copy link
Copy Markdown
Contributor

@reiern70 The problem was in the decorator. It did not preserve the nonce. The decorator was not needed at all. I've just pushed a fix on your branch that resolves the engine directly from the session and it works now.

@reiern70

reiern70 commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

@reiern70 The problem was in the decorator. It did not preserve the nonce. The decorator was not needed at all. I've just pushed a fix on your branch that resolves the engine directly from the session and it works now.

Many thanks!

@reiern70
reiern70 force-pushed the reiern70/plain-js branch 3 times, most recently from 474e847 to 4f7000f Compare September 8, 2026 16:45
coreJs = [
'../../wicket-core/src/main/java/org/apache/wicket/ajax/res/js/wicket-ajax-jquery-debug.js',
'../../wicket-core/src/main/java/org/apache/wicket/ajax/res/js/wicket-ajax-jquery.js',
'../../wicket-core/src/main/java/org/apache/wicket/ajax/res/js/wicket-ajax-vanilla.js',

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.

Minor nit: How about simply calling it wicket-ajax.js?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ok. Fine by me :-)

@mattrpav

mattrpav commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Love this!

reiern70 and others added 3 commits September 8, 2026 13:01
#1583

Wicket's client-side Ajax/event support (Wicket.Ajax, Wicket.Event, Wicket.DOM,
Wicket.Form, Wicket.Head, Wicket.Focus, ...) has always required jQuery. This
adds a second, plain-JavaScript implementation exposing the exact same API, so
applications that don't want to ship jQuery at all can opt in via:

    getJavaScriptLibrarySettings().setWicketAjaxReference(WicketAjaxVanillaResourceReference.get());

jQuery stays the default; existing applications are unaffected.

Core changes:
- wicket-ajax-vanilla.js: full port of wicket-ajax-jquery.js onto native
  XMLHttpRequest/DOM/event APIs (WicketAjaxVanillaResourceReference.java is its
  resource reference).
- Removed jQuery from every other framework JS file that only used it
  incidentally (not as its actual implementation choice), so the whole stack
  can run without jQuery once the vanilla engine is selected: the extensions
  Ajax components (autocomplete, palette, upload progress bar, ajax download,
  trap focus), the dev debug bar, native WebSockets support, and
  FileUploadToResourceField.js - all verified to still work correctly under
  the default jQuery engine too, and their public extension points/callback
  signatures are unchanged.
- Cleaned up several now-dead legacy-browser fallbacks and deprecated Web APIs
  encountered along the way (window.escape/unescape, window.event,
  Attr.specified, IE-only branches).

Testing:
- Ported the wicket-core QUnit suite (channels/dom/event/form/head/ajax/timer)
  off jQuery-based test-authoring helpers so the same tests run against either
  engine; all.html now takes ?vanilla alongside the existing ?<jquery-version>,
  and the Gruntfile has a matching qunit:vanilla target.
- Ran the full suite for real (jQuery 3.7.1, jQuery 4.0.0, vanilla) via a
  portable Node build + jsdom against a real HTTP server, since no JS runtime
  was available here otherwise - this caught and fixed several real bugs in
  the vanilla engine (event "extra data" colliding with native Event.detail,
  beforeSend's settings.url/data not reflecting the resolved request,
  unsupported dataType 'json', a DOM.show() default-display mismatch, and
  scripts in replaced/appended markup never executing).

Examples:
- wicket-examples can now be started with either engine via a single
  JVM-wide switch (-Dwicket.examples.use=JQUERY|VANILLA, default JQUERY),
  applied through AjaxEngineSelector from every example Application's
  init(), with a startup console banner (mirroring Wicket's own dev-mode
  warning) confirming which engine is active.
- The shared example page header also shows the active engine and lets a
  visitor switch it for their session only, live, without an application
  restart - implemented via an IHeaderResponseDecorator rather than mutating
  the shared JavaScriptLibrarySettings, so concurrent sessions with different
  choices never interfere with each other. Verified end-to-end with
  WicketTester (both switch directions render the correct script tags in the
  correct order, and the toggle link updates the session as expected).

Documentation: new user guide section (ajax_11.adoc) explaining both
implementations and how to switch; corrected IAjaxCallListener/ajax_5/ajax_6
docs that assumed jqXHR/jqEvent are always jQuery-specific objects.
The engine toggle in WicketExampleHeader was implemented as a header
response decorator that substituted the session's wicket-ajax.js
reference for the application's configured one. Both that decorator and
CSPNonceHeaderResponseDecorator are registered with
addPreResourceAggregationDecorator, and CSP registers later (from
WebApplication#validateInit, after init()), so it ends up upstream:
it stamped the nonce onto the item the selector then threw away, and
rendered a fresh JavaScriptHeaderItem with a null nonce in its place.
Under script-src 'strict-dynamic' 'nonce-XYZ' the browser blocked that
script, so switching to the vanilla engine loaded no wicket-ajax.js at
all - which is why the examples had CSP turned off entirely.

Resolve the engine at the source instead: give the application a
JavaScriptLibrarySettings whose getWicketAjaxReference() returns
referenceFor(getEffectiveEngine()) on every call. Nothing is
substituted after the fact, so the nonce is applied normally. The
instance is shared by all sessions but holds no state - it just
forwards to whatever Session#get() resolves to on the calling thread -
so concurrent sessions with different choices still do not interfere.

Answering at the source also fixes a second defect: because
OnDomReadyHeaderItem, OnLoadHeaderItem and OnEventHeaderItem declare a
dependency on the configured reference, the aggregator used to pull
jQuery back in behind the decorator's back, and a session switched to
VANILLA still downloaded jQuery. It no longer does.

With that, restore the CSP configuration the examples had before the
vanilla engine was added.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@reiern70

reiern70 commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

Love this!

Thanks. It seems to be working. I need some more testing. If someone else tries it I would be happy to receive feedback.

return;
}

var isUndef = function (target) {

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.

Maybe it worth to use modern let/const syntax? :)

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