mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-03 08:08:43 +00:00
LibWeb: Add "position: sticky" support
Sticky positioning is implemented by modifying the algorithm for assigning and refreshing scroll frames. Now, elements with "position: sticky" are assigned their own scroll frame, and their position is refreshed independently from regular scroll boxes. Refreshing the scroll offsets for sticky boxes does not require display list invalidation. A separate hash map is used for the scroll frames of sticky boxes. This is necessary because a single paintable box can have two scroll frames if it 1) has "position: sticky" and 2) contains scrollable overflow.
This commit is contained in:
parent
866608532a
commit
30b636e90b
Notes:
github-actions[bot]
2024-08-30 17:03:56 +00:00
Author: https://github.com/kalenikaliaksandr
Commit: 30b636e90b
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/1229
20 changed files with 705 additions and 2 deletions
53
Tests/LibWeb/Ref/position-sticky-bottom.html
Normal file
53
Tests/LibWeb/Ref/position-sticky-bottom.html
Normal file
|
@ -0,0 +1,53 @@
|
|||
<!DOCTYPE html>
|
||||
<link rel="match" href="reference/position-sticky-bottom-ref.html" />
|
||||
<style>
|
||||
.scrollable-container {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
overflow-y: scroll;
|
||||
border: 5px solid red;
|
||||
}
|
||||
|
||||
.sticky-box {
|
||||
position: sticky;
|
||||
bottom: 50px;
|
||||
width: 240px;
|
||||
height: 80px;
|
||||
background: #3498db;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.box {
|
||||
height: 500px;
|
||||
background-color: orange;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="scrollable-container" id="sticky-is-below-scrollport">
|
||||
<div class="box"></div>
|
||||
<div class="sticky-box">I stick within this scrollable box!</div>
|
||||
<div class="box"></div>
|
||||
</div>
|
||||
|
||||
<div class="scrollable-container" id="sticky-is-inside-scrollport">
|
||||
<div class="box"></div>
|
||||
<div class="sticky-box">I stick within this scrollable box!</div>
|
||||
<div class="box"></div>
|
||||
</div>
|
||||
|
||||
<div class="scrollable-container" id="sticky-is-above-scrollport">
|
||||
<div class="box"></div>
|
||||
<div class="sticky-box">I stick within this scrollable box!</div>
|
||||
<div class="box"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const stickyIsBelowScrollport = document.getElementById("sticky-is-below-scrollport");
|
||||
stickyIsBelowScrollport.scrollTop = 0;
|
||||
|
||||
const stickyIsInsideScrollport = document.getElementById("sticky-is-inside-scrollport");
|
||||
stickyIsInsideScrollport.scrollTop = 390;
|
||||
|
||||
const stickyIsAboveScrollport = document.getElementById("sticky-is-above-scrollport");
|
||||
stickyIsAboveScrollport.scrollTop = 780;
|
||||
</script>
|
56
Tests/LibWeb/Ref/position-sticky-left.html
Normal file
56
Tests/LibWeb/Ref/position-sticky-left.html
Normal file
|
@ -0,0 +1,56 @@
|
|||
<!DOCTYPE html>
|
||||
<link rel="match" href="reference/position-sticky-left-ref.html" />
|
||||
<style>
|
||||
* {
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.scroll-container {
|
||||
width: 500px;
|
||||
overflow-x: scroll;
|
||||
white-space: nowrap;
|
||||
background-color: #f0f0f0;
|
||||
display: grid;
|
||||
grid-template-columns: 1000px 300px 1000px;
|
||||
border: 5px solid yellowgreen;
|
||||
}
|
||||
|
||||
.section {
|
||||
height: 200px;
|
||||
background-color: orangered;
|
||||
}
|
||||
|
||||
.sticky-element {
|
||||
position: sticky;
|
||||
left: 0;
|
||||
background-color: blueviolet;
|
||||
height: 200px;
|
||||
line-height: 200px;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
<div class="scroll-container" id="a">
|
||||
<div class="section"></div>
|
||||
<div class="sticky-element"></div>
|
||||
<div class="section"></div>
|
||||
</div>
|
||||
<div class="scroll-container" id="b">
|
||||
<div class="section"></div>
|
||||
<div class="sticky-element"></div>
|
||||
<div class="section"></div>
|
||||
</div>
|
||||
<div class="scroll-container" id="c">
|
||||
<div class="section"></div>
|
||||
<div class="sticky-element"></div>
|
||||
<div class="section"></div>
|
||||
</div>
|
||||
<script>
|
||||
const a = document.getElementById("a");
|
||||
a.scrollLeft = 0;
|
||||
|
||||
const b = document.getElementById("b");
|
||||
b.scrollLeft = 900;
|
||||
|
||||
const c = document.getElementById("c");
|
||||
c.scrollLeft = 1400;
|
||||
</script>
|
56
Tests/LibWeb/Ref/position-sticky-right.html
Normal file
56
Tests/LibWeb/Ref/position-sticky-right.html
Normal file
|
@ -0,0 +1,56 @@
|
|||
<!DOCTYPE html>
|
||||
<link rel="match" href="reference/position-sticky-right-ref.html" />
|
||||
<style>
|
||||
* {
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.scroll-container {
|
||||
width: 500px;
|
||||
overflow-x: scroll;
|
||||
white-space: nowrap;
|
||||
background-color: #f0f0f0;
|
||||
display: grid;
|
||||
grid-template-columns: 1000px 300px 1000px;
|
||||
border: 5px solid yellowgreen;
|
||||
}
|
||||
|
||||
.section {
|
||||
height: 200px;
|
||||
background-color: orangered;
|
||||
}
|
||||
|
||||
.sticky-element {
|
||||
position: sticky;
|
||||
right: 0;
|
||||
background-color: blueviolet;
|
||||
height: 200px;
|
||||
line-height: 200px;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
<div class="scroll-container" id="a">
|
||||
<div class="section"></div>
|
||||
<div class="sticky-element"></div>
|
||||
<div class="section"></div>
|
||||
</div>
|
||||
<div class="scroll-container" id="b">
|
||||
<div class="section"></div>
|
||||
<div class="sticky-element"></div>
|
||||
<div class="section"></div>
|
||||
</div>
|
||||
<div class="scroll-container" id="c">
|
||||
<div class="section"></div>
|
||||
<div class="sticky-element"></div>
|
||||
<div class="section"></div>
|
||||
</div>
|
||||
<script>
|
||||
const a = document.getElementById("a");
|
||||
a.scrollLeft = 0;
|
||||
|
||||
const b = document.getElementById("b");
|
||||
b.scrollLeft = 900;
|
||||
|
||||
const c = document.getElementById("c");
|
||||
c.scrollLeft = 1400;
|
||||
</script>
|
|
@ -0,0 +1,60 @@
|
|||
<!DOCTYPE html>
|
||||
<link rel="match" href="reference/position-sticky-should-stay-within-containing-block-ref.html" />
|
||||
<style>
|
||||
.scrollable-container {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
overflow-y: scroll;
|
||||
border: 5px solid red;
|
||||
}
|
||||
|
||||
.sticky-box {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 240px;
|
||||
height: 80px;
|
||||
background: #3498db;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.box {
|
||||
height: 500px;
|
||||
background-color: orange;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="scrollable-container" id="sticky-is-below-scrollport">
|
||||
<div class="box"></div>
|
||||
<div>
|
||||
<div class="sticky-box">I stick within this scrollable box!</div>
|
||||
</div>
|
||||
<div class="box"></div>
|
||||
</div>
|
||||
|
||||
<div class="scrollable-container" id="sticky-is-inside-scrollport">
|
||||
<div class="box"></div>
|
||||
<div>
|
||||
<div class="sticky-box">I stick within this scrollable box!</div>
|
||||
</div>
|
||||
<div class="box"></div>
|
||||
</div>
|
||||
|
||||
<div class="scrollable-container" id="sticky-is-above-scrollport">
|
||||
<div class="box"></div>
|
||||
<div>
|
||||
<div class="sticky-box">I stick within this scrollable box!</div>
|
||||
</div>
|
||||
<div class="box"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const stickyIsBelowScrollport = document.getElementById("sticky-is-below-scrollport");
|
||||
stickyIsBelowScrollport.scrollTop = 0;
|
||||
|
||||
const stickyIsInsideScrollport = document.getElementById("sticky-is-inside-scrollport");
|
||||
stickyIsInsideScrollport.scrollTop = 390;
|
||||
|
||||
const stickyIsAboveScrollport = document.getElementById("sticky-is-above-scrollport");
|
||||
stickyIsAboveScrollport.scrollTop = 780;
|
||||
</script>
|
53
Tests/LibWeb/Ref/position-sticky-top.html
Normal file
53
Tests/LibWeb/Ref/position-sticky-top.html
Normal file
|
@ -0,0 +1,53 @@
|
|||
<!DOCTYPE html>
|
||||
<link rel="match" href="reference/position-sticky-top-ref.html" />
|
||||
<style>
|
||||
.scrollable-container {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
overflow-y: scroll;
|
||||
border: 5px solid red;
|
||||
}
|
||||
|
||||
.sticky-box {
|
||||
position: sticky;
|
||||
top: 50px;
|
||||
width: 240px;
|
||||
height: 80px;
|
||||
background: #3498db;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.box {
|
||||
height: 500px;
|
||||
background-color: orange;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="scrollable-container" id="sticky-is-below-scrollport">
|
||||
<div class="box"></div>
|
||||
<div class="sticky-box">I stick within this scrollable box!</div>
|
||||
<div class="box"></div>
|
||||
</div>
|
||||
|
||||
<div class="scrollable-container" id="sticky-is-inside-scrollport">
|
||||
<div class="box"></div>
|
||||
<div class="sticky-box">I stick within this scrollable box!</div>
|
||||
<div class="box"></div>
|
||||
</div>
|
||||
|
||||
<div class="scrollable-container" id="sticky-is-above-scrollport">
|
||||
<div class="box"></div>
|
||||
<div class="sticky-box">I stick within this scrollable box!</div>
|
||||
<div class="box"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const stickyIsBelowScrollport = document.getElementById("sticky-is-below-scrollport");
|
||||
stickyIsBelowScrollport.scrollTop = 0;
|
||||
|
||||
const stickyIsInsideScrollport = document.getElementById("sticky-is-inside-scrollport");
|
||||
stickyIsInsideScrollport.scrollTop = 390;
|
||||
|
||||
const stickyIsAboveScrollport = document.getElementById("sticky-is-above-scrollport");
|
||||
stickyIsAboveScrollport.scrollTop = 780;
|
||||
</script>
|
58
Tests/LibWeb/Ref/reference/position-sticky-bottom-ref.html
Normal file
58
Tests/LibWeb/Ref/reference/position-sticky-bottom-ref.html
Normal file
|
@ -0,0 +1,58 @@
|
|||
<!DOCTYPE html>
|
||||
<style>
|
||||
.scrollable-container {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
overflow-y: scroll;
|
||||
border: 5px solid red;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sticky-box {
|
||||
width: 240px;
|
||||
height: 80px;
|
||||
background: #3498db;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.box {
|
||||
height: 500px;
|
||||
background-color: orange;
|
||||
}
|
||||
|
||||
.fill-abspos-box-space {
|
||||
height: 80px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="scrollable-container" id="sticky-is-below-scrollport">
|
||||
<div class="box"></div>
|
||||
<div class="sticky-box" style="position: absolute; bottom: 50px">
|
||||
I stick within this scrollable box!
|
||||
</div>
|
||||
<div class="fill-abspos-box-space"></div>
|
||||
<div class="box"></div>
|
||||
</div>
|
||||
|
||||
<div class="scrollable-container" id="sticky-is-inside-scrollport">
|
||||
<div class="box"></div>
|
||||
<div class="sticky-box">I stick within this scrollable box!</div>
|
||||
<div class="box"></div>
|
||||
</div>
|
||||
|
||||
<div class="scrollable-container" id="sticky-is-above-scrollport">
|
||||
<div class="box"></div>
|
||||
<div class="sticky-box">I stick within this scrollable box!</div>
|
||||
<div class="box"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const stickyIsBelowScrollport = document.getElementById("sticky-is-below-scrollport");
|
||||
stickyIsBelowScrollport.scrollTop = 0;
|
||||
|
||||
const stickyIsInsideScrollport = document.getElementById("sticky-is-inside-scrollport");
|
||||
stickyIsInsideScrollport.scrollTop = 390;
|
||||
|
||||
const stickyIsAboveScrollport = document.getElementById("sticky-is-above-scrollport");
|
||||
stickyIsAboveScrollport.scrollTop = 780;
|
||||
</script>
|
43
Tests/LibWeb/Ref/reference/position-sticky-left-ref.html
Normal file
43
Tests/LibWeb/Ref/reference/position-sticky-left-ref.html
Normal file
|
@ -0,0 +1,43 @@
|
|||
<!DOCTYPE html>
|
||||
<style>
|
||||
* {
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.scroll-container {
|
||||
width: 500px;
|
||||
overflow-x: scroll;
|
||||
white-space: nowrap;
|
||||
background-color: #f0f0f0;
|
||||
display: flex;
|
||||
border: 5px solid yellowgreen;
|
||||
}
|
||||
|
||||
.section {
|
||||
flex: 0 0 1000px;
|
||||
height: 200px;
|
||||
background-color: orangered;
|
||||
}
|
||||
|
||||
.sticky-element {
|
||||
position: sticky;
|
||||
left: 0;
|
||||
background-color: blueviolet;
|
||||
flex: 0 0 300px;
|
||||
height: 200px;
|
||||
line-height: 200px;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
<div class="scroll-container" id="a">
|
||||
<div class="section"></div>
|
||||
</div>
|
||||
<div class="scroll-container" id="b">
|
||||
<div class="section" style="flex-basis: 100px"></div>
|
||||
<div class="sticky-element"></div>
|
||||
<div class="section" style="flex-basis: 100px"></div>
|
||||
</div>
|
||||
<div class="scroll-container" id="c">
|
||||
<div class="sticky-element"></div>
|
||||
<div class="section"></div>
|
||||
</div>
|
43
Tests/LibWeb/Ref/reference/position-sticky-right-ref.html
Normal file
43
Tests/LibWeb/Ref/reference/position-sticky-right-ref.html
Normal file
|
@ -0,0 +1,43 @@
|
|||
<!DOCTYPE html>
|
||||
<style>
|
||||
* {
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.scroll-container {
|
||||
width: 500px;
|
||||
overflow-x: scroll;
|
||||
white-space: nowrap;
|
||||
background-color: #f0f0f0;
|
||||
display: flex;
|
||||
border: 5px solid yellowgreen;
|
||||
}
|
||||
|
||||
.section {
|
||||
flex: 0 0 1000px;
|
||||
height: 200px;
|
||||
background-color: orangered;
|
||||
}
|
||||
|
||||
.sticky-element {
|
||||
position: sticky;
|
||||
left: 0;
|
||||
background-color: blueviolet;
|
||||
flex: 0 0 300px;
|
||||
height: 200px;
|
||||
line-height: 200px;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
<div class="scroll-container" id="a">
|
||||
<div class="section" style="flex-basis: 200px"></div>
|
||||
<div class="sticky-element"></div>
|
||||
</div>
|
||||
<div class="scroll-container" id="b">
|
||||
<div class="section" style="flex-basis: 100px"></div>
|
||||
<div class="sticky-element"></div>
|
||||
<div class="section" style="flex-basis: 100px"></div>
|
||||
</div>
|
||||
<div class="scroll-container" id="c">
|
||||
<div class="section"></div>
|
||||
</div>
|
|
@ -0,0 +1,51 @@
|
|||
<!DOCTYPE html>
|
||||
<style>
|
||||
.scrollable-container {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
overflow-y: scroll;
|
||||
border: 5px solid red;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sticky-box {
|
||||
width: 240px;
|
||||
height: 80px;
|
||||
background: #3498db;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.box {
|
||||
height: 500px;
|
||||
background-color: orange;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="scrollable-container" id="sticky-is-below-scrollport">
|
||||
<div class="box"></div>
|
||||
<div class="sticky-box">I stick within this scrollable box!</div>
|
||||
<div class="box"></div>
|
||||
</div>
|
||||
|
||||
<div class="scrollable-container" id="sticky-is-inside-scrollport">
|
||||
<div class="box"></div>
|
||||
<div class="sticky-box">I stick within this scrollable box!</div>
|
||||
<div class="box"></div>
|
||||
</div>
|
||||
|
||||
<div class="scrollable-container" id="sticky-is-above-scrollport">
|
||||
<div class="box"></div>
|
||||
<div class="sticky-box">I stick within this scrollable box!</div>
|
||||
<div class="box"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const stickyIsBelowScrollport = document.getElementById("sticky-is-below-scrollport");
|
||||
stickyIsBelowScrollport.scrollTop = 0;
|
||||
|
||||
const stickyIsInsideScrollport = document.getElementById("sticky-is-inside-scrollport");
|
||||
stickyIsInsideScrollport.scrollTop = 390;
|
||||
|
||||
const stickyIsAboveScrollport = document.getElementById("sticky-is-above-scrollport");
|
||||
stickyIsAboveScrollport.scrollTop = 780;
|
||||
</script>
|
58
Tests/LibWeb/Ref/reference/position-sticky-top-ref.html
Normal file
58
Tests/LibWeb/Ref/reference/position-sticky-top-ref.html
Normal file
|
@ -0,0 +1,58 @@
|
|||
<!DOCTYPE html>
|
||||
<style>
|
||||
.scrollable-container {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
overflow-y: scroll;
|
||||
border: 5px solid red;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sticky-box {
|
||||
width: 240px;
|
||||
height: 80px;
|
||||
background: #3498db;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.box {
|
||||
height: 500px;
|
||||
background-color: orange;
|
||||
}
|
||||
|
||||
.fill-abspos-box-space {
|
||||
height: 80px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="scrollable-container" id="sticky-is-below-scrollport">
|
||||
<div class="box"></div>
|
||||
<div class="sticky-box">I stick within this scrollable box!</div>
|
||||
<div class="box"></div>
|
||||
</div>
|
||||
|
||||
<div class="scrollable-container" id="sticky-is-inside-scrollport">
|
||||
<div class="box"></div>
|
||||
<div class="sticky-box">I stick within this scrollable box!</div>
|
||||
<div class="box"></div>
|
||||
</div>
|
||||
|
||||
<div class="scrollable-container" id="sticky-is-above-scrollport">
|
||||
<div class="box"></div>
|
||||
<div class="sticky-box" style="position: absolute; top: 830px">
|
||||
I stick within this scrollable box!
|
||||
</div>
|
||||
<div class="fill-abspos-box-space"></div>
|
||||
<div class="box"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const stickyIsBelowScrollport = document.getElementById("sticky-is-below-scrollport");
|
||||
stickyIsBelowScrollport.scrollTop = 0;
|
||||
|
||||
const stickyIsInsideScrollport = document.getElementById("sticky-is-inside-scrollport");
|
||||
stickyIsInsideScrollport.scrollTop = 390;
|
||||
|
||||
const stickyIsAboveScrollport = document.getElementById("sticky-is-above-scrollport");
|
||||
stickyIsAboveScrollport.scrollTop = 780;
|
||||
</script>
|
Loading…
Add table
Add a link
Reference in a new issue