Skip to content

Commit 656c627

Browse files
committed
scrollIntoView() now triggers a scroll event on the parent.
For FF scroll events are of type UIEvent. Scroll events are not bubbling and not cancelable. see #942
1 parent 5561b48 commit 656c627

File tree

6 files changed

+118
-5
lines changed

6 files changed

+118
-5
lines changed

src/changes/changes.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@
88

99
<body>
1010
<release version="4.12.0" date="April xx, 2025" description="Chrome/Edge 135, Firefox 137, Rhino RegExp, Bugfixes">
11+
<action type="update" dev="rbri">
12+
scrollIntoView() now triggers a scroll event on the parent.
13+
</action>
14+
<action type="update" dev="rbri">
15+
For FF scroll events are of type UIEvent.
16+
</action>
17+
<action type="fix" dev="rbri">
18+
Scroll events are not bubbling and not cancelable.
19+
</action>
1120
<action type="fix" dev="RhinoTeam">
1221
core-js: Function.prototype[Symbol.hasInstance] fixed.
1322
</action>

src/main/java/org/htmlunit/BrowserVersionFeatures.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ public enum BrowserVersionFeatures {
9090
@BrowserFeature({FF, FF_ESR})
9191
EVENT_ONPOPSTATE_DOCUMENT_CREATE_NOT_SUPPORTED,
9292

93+
/** Scroll events are of type 'UIEvent'. */
94+
@BrowserFeature({FF, FF_ESR})
95+
EVENT_SCROLL_UIEVENT,
96+
9397
/** Supports event type 'TextEvent'. */
9498
@BrowserFeature({FF, FF_ESR})
9599
EVENT_TYPE_MUTATIONEVENT,

src/main/java/org/htmlunit/javascript/host/Element.java

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515
package org.htmlunit.javascript.host;
1616

17+
import static org.htmlunit.BrowserVersionFeatures.EVENT_SCROLL_UIEVENT;
1718
import static org.htmlunit.BrowserVersionFeatures.JS_OUTER_HTML_THROWS_FOR_DETACHED;
1819
import static org.htmlunit.html.DomElement.ATTRIBUTE_NOT_DEFINED;
1920
import static org.htmlunit.javascript.configuration.SupportedBrowser.CHROME;
@@ -64,6 +65,7 @@
6465
import org.htmlunit.javascript.host.dom.NodeList;
6566
import org.htmlunit.javascript.host.event.Event;
6667
import org.htmlunit.javascript.host.event.EventHandler;
68+
import org.htmlunit.javascript.host.event.UIEvent;
6769
import org.htmlunit.javascript.host.html.HTMLCollection;
6870
import org.htmlunit.javascript.host.html.HTMLElement;
6971
import org.htmlunit.javascript.host.html.HTMLElement.ProxyDomNode;
@@ -1234,8 +1236,18 @@ public void scrollBy(final Scriptable x, final Scriptable y) {
12341236
setScrollLeft(getScrollLeft() + xOff);
12351237
setScrollTop(getScrollTop() + yOff);
12361238

1237-
final Event event = new Event(this, Event.TYPE_SCROLL);
1238-
fireEvent(event);
1239+
fireScrollEvent(this);
1240+
}
1241+
1242+
private void fireScrollEvent(final Node node) {
1243+
final Event event;
1244+
if (getBrowserVersion().hasFeature(EVENT_SCROLL_UIEVENT)) {
1245+
event = new UIEvent(node, Event.TYPE_SCROLL);
1246+
}
1247+
else {
1248+
event = new Event(node, Event.TYPE_SCROLL);
1249+
}
1250+
node.fireEvent(event);
12391251
}
12401252

12411253
/**
@@ -1269,8 +1281,7 @@ public void scrollTo(final Scriptable x, final Scriptable y) {
12691281
setScrollLeft(xOff);
12701282
setScrollTop(yOff);
12711283

1272-
final Event event = new Event(this, Event.TYPE_SCROLL);
1273-
fireEvent(event);
1284+
fireScrollEvent(this);
12741285
}
12751286

12761287
/**
@@ -1280,7 +1291,17 @@ public void scrollTo(final Scriptable x, final Scriptable y) {
12801291
*/
12811292
@JsxFunction
12821293
public void scrollIntoView() {
1283-
/* do nothing at the moment */
1294+
/* do nothing at the moment, only trigger the scroll event */
1295+
1296+
Node parent = getParent();
1297+
while (parent != null) {
1298+
if (parent instanceof HTMLElement) {
1299+
fireScrollEvent(parent);
1300+
return;
1301+
}
1302+
1303+
parent = parent.getParent();
1304+
}
12841305
}
12851306

12861307
/**

src/main/java/org/htmlunit/javascript/host/event/Event.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,7 @@ else if (TYPE_ERROR.equals(type)) {
562562
else if (
563563
TYPE_FOCUS.equals(type)
564564
|| TYPE_BLUR.equals(type)
565+
|| TYPE_SCROLL.equals(type)
565566
|| TYPE_BEFOREPRINT.equals(type)
566567
|| TYPE_AFTERPRINT.equals(type)) {
567568
bubbles_ = false;

src/test/java/org/htmlunit/javascript/host/event/EventTest.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1518,4 +1518,43 @@ public void domEventNameUsedAsFunctionName() throws Exception {
15181518

15191519
loadPageVerifyTitle2(html);
15201520
}
1521+
1522+
/**
1523+
* Tests that JavaScript scrollIntoView() scrollEvent.
1524+
* @throws Exception if the test fails
1525+
*/
1526+
@Test
1527+
@Alerts(DEFAULT = {"[object Event]", "scroll", "false", "false", "false"},
1528+
FF = {"[object UIEvent]", "scroll", "false", "true", "false"},
1529+
FF_ESR = {"[object UIEvent]", "scroll", "false", "true", "false"})
1530+
@HtmlUnitNYI(FF = {"[object UIEvent]", "scroll", "false", "false", "false"},
1531+
FF_ESR = {"[object UIEvent]", "scroll", "false", "false", "false"})
1532+
public void scrollEvent() throws Exception {
1533+
final String html = DOCTYPE_HTML
1534+
+ "<html>\n"
1535+
+ "<head>\n"
1536+
+ "<script>\n"
1537+
+ LOG_TITLE_FUNCTION
1538+
+ DUMP_EVENT_FUNCTION
1539+
+ "</script>\n"
1540+
+ "</head>\n"
1541+
1542+
+ "<body>\n"
1543+
+ " <div id='container' style='overflow-y: scroll; height: 100px;'>\n"
1544+
+ " <div style='height: 1000px;'>spacer</div>\n"
1545+
+ " <div id='target' style='background: red;'>Target</div>"
1546+
+ " </div>\n"
1547+
1548+
+ " <script>\n"
1549+
+ " var c = document.getElementById('container');\n"
1550+
+ " c.addEventListener('scroll', function(e) { dump(e); });\n"
1551+
1552+
+ " var s = document.getElementById('target');"
1553+
+ " s.scrollIntoView();\n"
1554+
+ " </script>\n"
1555+
+ "</body>\n"
1556+
+ "</html>";
1557+
1558+
loadPageVerifyTitle2(html);
1559+
}
15211560
}

src/test/java/org/htmlunit/javascript/host/html/HTMLElementTest.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2156,6 +2156,45 @@ public void scrollIntoView() throws Exception {
21562156
loadPageVerifyTitle2(html);
21572157
}
21582158

2159+
/**
2160+
* Tests that JavaScript scrollIntoView() scrollEvent.
2161+
* @throws Exception if the test fails
2162+
*/
2163+
@Test
2164+
@Alerts("container [object HTMLDivElement]")
2165+
public void scrollIntoViewTriggersOnScroll() throws Exception {
2166+
final String html = DOCTYPE_HTML
2167+
+ "<html>\n"
2168+
+ "<head>\n"
2169+
+ "<script>\n"
2170+
+ LOG_TITLE_FUNCTION
2171+
+ "</script>\n"
2172+
+ "</head>\n"
2173+
2174+
+ "<body>\n"
2175+
+ " <div id='container' style='overflow-y: scroll; height: 100px;'>\n"
2176+
+ " <div style='height: 1000px;'>spacer</div>\n"
2177+
+ " <div id='target' style='background: red;'>Target</div>"
2178+
+ " </div>\n"
2179+
2180+
+ " <script>\n"
2181+
+ " document.addEventListener('scroll', function(e) { log('document ' + e.target) });\n"
2182+
+ " document.body.addEventListener('scroll', function(e) { log('body ' + e.target) });\n"
2183+
2184+
+ " var c = document.getElementById('container');\n"
2185+
+ " c.addEventListener('scroll', function(e) { log('container ' + e.target) });\n"
2186+
2187+
+ " var s = document.getElementById('target');"
2188+
+ " s.addEventListener('scroll', function(e) { log('target ' + e.target) });\n"
2189+
2190+
+ " s.scrollIntoView();\n"
2191+
+ " </script>\n"
2192+
+ "</body>\n"
2193+
+ "</html>";
2194+
2195+
loadPageVerifyTitle2(html);
2196+
}
2197+
21592198
/**
21602199
* Tests the offsetParent property.
21612200
* @throws Exception if the test fails

0 commit comments

Comments
 (0)