Skip to content

Commit 759bc24

Browse files
patrickhlaukeXhmikosR
authored andcommitted
Manual backport of #32661
Make carousel indicators actual buttons
1 parent 8133c3e commit 759bc24

File tree

7 files changed

+72
-65
lines changed

7 files changed

+72
-65
lines changed

js/src/carousel.js

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ const SELECTOR_ITEM = '.carousel-item'
7777
const SELECTOR_ITEM_IMG = '.carousel-item img'
7878
const SELECTOR_NEXT_PREV = '.carousel-item-next, .carousel-item-prev'
7979
const SELECTOR_INDICATORS = '.carousel-indicators'
80+
const SELECTOR_INDICATOR = '[data-target]'
8081
const SELECTOR_DATA_SLIDE = '[data-slide], [data-slide-to]'
8182
const SELECTOR_DATA_RIDE = '[data-ride="carousel"]'
8283

@@ -388,15 +389,18 @@ class Carousel {
388389

389390
_setActiveIndicatorElement(element) {
390391
if (this._indicatorsElement) {
391-
const indicators = [].slice.call(this._indicatorsElement.querySelectorAll(SELECTOR_ACTIVE))
392-
$(indicators).removeClass(CLASS_NAME_ACTIVE)
392+
const activeIndicator = this._indicatorsElement.querySelector(SELECTOR_ACTIVE)
393+
$(activeIndicator).removeClass(CLASS_NAME_ACTIVE)
394+
$(activeIndicator).removeAttr('aria-current')
393395

394-
const nextIndicator = this._indicatorsElement.children[
395-
this._getItemIndex(element)
396-
]
396+
const indicators = this._indicatorsElement.querySelectorAll(SELECTOR_INDICATOR)
397397

398-
if (nextIndicator) {
399-
$(nextIndicator).addClass(CLASS_NAME_ACTIVE)
398+
for (let i = 0; i < indicators.length; i++) {
399+
if (parseInt(indicators[i].getAttribute('data-slide-to'), 10) === this._getItemIndex(element)) {
400+
$(indicators[i]).addClass(CLASS_NAME_ACTIVE)
401+
$(indicators[i]).attr('aria-current', 'true')
402+
break
403+
}
400404
}
401405
}
402406
}

js/tests/integration/index.html

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ <h1>Hello, world!</h1>
2222
Tooltip on top
2323
</button>
2424
<div id="carouselExampleCaptions" class="carousel slide mt-2" data-ride="carousel">
25-
<ol class="carousel-indicators">
26-
<li data-target="#carouselExampleCaptions" data-slide-to="0"></li>
27-
<li data-target="#carouselExampleCaptions" data-slide-to="1" class="active"></li>
28-
<li data-target="#carouselExampleCaptions" data-slide-to="2"></li>
29-
</ol>
25+
<div class="carousel-indicators">
26+
<button type="button" data-target="#carouselExampleIndicators" data-slide-to="0" aria-label="Slide 1"></button>
27+
<button type="button" data-target="#carouselExampleIndicators" data-slide-to="1" class="active" aria-current="true" aria-label="Slide 2"></button>
28+
<button type="button" data-target="#carouselExampleIndicators" data-slide-to="2" aria-label="Slide 3"></button>
29+
</div>
3030
<div class="carousel-inner">
3131
<div class="carousel-item">
3232
<img class="d-block w-100" alt="First slide [800x400]" src="data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20800%20400%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_167a6f826cb%20text%20%7B%20fill%3A%23555%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A40pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_167a6f826cb%22%3E%3Crect%20width%3D%22800%22%20height%3D%22400%22%20fill%3D%22%23777%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22285.921875%22%20y%3D%22217.7%22%3EFirst%20slide%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E">

js/tests/unit/carousel.js

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,11 @@ $(function () {
127127
QUnit.test('should reset when slide is prevented', function (assert) {
128128
assert.expect(6)
129129
var carouselHTML = '<div id="carousel-example-generic" class="carousel slide">' +
130-
'<ol class="carousel-indicators">' +
131-
'<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>' +
132-
'<li data-target="#carousel-example-generic" data-slide-to="1"/>' +
133-
'<li data-target="#carousel-example-generic" data-slide-to="2"/>' +
134-
'</ol>' +
130+
'<div class="carousel-indicators">' +
131+
'<button data-target="#carousel-example-generic" data-slide-to="0" class="active" aria-current="true"></button>' +
132+
'<button data-target="#carousel-example-generic" data-slide-to="1"></button>' +
133+
'<button data-target="#carousel-example-generic" data-slide-to="2"></button>' +
134+
'</div>' +
135135
'<div class="carousel-inner">' +
136136
'<div class="carousel-item active">' +
137137
'<div class="carousel-caption"></div>' +
@@ -154,16 +154,16 @@ $(function () {
154154
e.preventDefault()
155155
setTimeout(function () {
156156
assert.true($carousel.find('.carousel-item:nth-child(1)').is('.active'), 'first item still active')
157-
assert.true($carousel.find('.carousel-indicators li:nth-child(1)').is('.active'), 'first indicator still active')
157+
assert.true($carousel.find('.carousel-indicators button:nth-child(1)').is('.active'), 'first indicator still active')
158158
$carousel.bootstrapCarousel('next')
159159
}, 0)
160160
})
161161
.one('slid.bs.carousel', function () {
162162
setTimeout(function () {
163163
assert.false($carousel.find('.carousel-item:nth-child(1)').is('.active'), 'first item still active')
164-
assert.false($carousel.find('.carousel-indicators li:nth-child(1)').is('.active'), 'first indicator still active')
164+
assert.false($carousel.find('.carousel-indicators button:nth-child(1)').is('.active'), 'first indicator still active')
165165
assert.true($carousel.find('.carousel-item:nth-child(2)').is('.active'), 'second item active')
166-
assert.true($carousel.find('.carousel-indicators li:nth-child(2)').is('.active'), 'second indicator active')
166+
assert.true($carousel.find('.carousel-indicators button:nth-child(2)').is('.active'), 'second indicator active')
167167
done()
168168
}, 0)
169169
})
@@ -769,11 +769,11 @@ $(function () {
769769
QUnit.test('should wrap around from end to start when wrap option is true', function (assert) {
770770
assert.expect(3)
771771
var carouselHTML = '<div id="carousel-example-generic" class="carousel slide" data-wrap="true">' +
772-
'<ol class="carousel-indicators">' +
773-
'<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>' +
774-
'<li data-target="#carousel-example-generic" data-slide-to="1"/>' +
775-
'<li data-target="#carousel-example-generic" data-slide-to="2"/>' +
776-
'</ol>' +
772+
'<div class="carousel-indicators">' +
773+
'<button data-target="#carousel-example-generic" data-slide-to="0" class="active" aria-current="true"></button>' +
774+
'<button data-target="#carousel-example-generic" data-slide-to="1"></button>' +
775+
'<button data-target="#carousel-example-generic" data-slide-to="2"></button>' +
776+
'</div>' +
777777
'<div class="carousel-inner">' +
778778
'<div class="carousel-item active" id="one">' +
779779
'<div class="carousel-caption"></div>' +
@@ -816,11 +816,11 @@ $(function () {
816816
QUnit.test('should wrap around from start to end when wrap option is true', function (assert) {
817817
assert.expect(1)
818818
var carouselHTML = '<div id="carousel-example-generic" class="carousel slide" data-wrap="true">' +
819-
'<ol class="carousel-indicators">' +
820-
'<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>' +
821-
'<li data-target="#carousel-example-generic" data-slide-to="1"/>' +
822-
'<li data-target="#carousel-example-generic" data-slide-to="2"/>' +
823-
'</ol>' +
819+
'<div class="carousel-indicators">' +
820+
'<button data-target="#carousel-example-generic" data-slide-to="0" class="active" aria-current="true"></button>' +
821+
'<button data-target="#carousel-example-generic" data-slide-to="1"></button>' +
822+
'<button data-target="#carousel-example-generic" data-slide-to="2"></button>' +
823+
'</div>' +
824824
'<div class="carousel-inner">' +
825825
'<div class="carousel-item active" id="one">' +
826826
'<div class="carousel-caption"></div>' +
@@ -850,11 +850,11 @@ $(function () {
850850
QUnit.test('should stay at the end when the next method is called and wrap is false', function (assert) {
851851
assert.expect(3)
852852
var carouselHTML = '<div id="carousel-example-generic" class="carousel slide" data-wrap="false">' +
853-
'<ol class="carousel-indicators">' +
854-
'<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>' +
855-
'<li data-target="#carousel-example-generic" data-slide-to="1"/>' +
856-
'<li data-target="#carousel-example-generic" data-slide-to="2"/>' +
857-
'</ol>' +
853+
'<div class="carousel-indicators">' +
854+
'<button data-target="#carousel-example-generic" data-slide-to="0" class="active" aria-current="true"></button>' +
855+
'<button data-target="#carousel-example-generic" data-slide-to="1"></button>' +
856+
'<button data-target="#carousel-example-generic" data-slide-to="2"></button>' +
857+
'</div>' +
858858
'<div class="carousel-inner">' +
859859
'<div class="carousel-item active" id="one">' +
860860
'<div class="carousel-caption"></div>' +
@@ -898,11 +898,11 @@ $(function () {
898898
QUnit.test('should stay at the start when the prev method is called and wrap is false', function (assert) {
899899
assert.expect(1)
900900
var carouselHTML = '<div id="carousel-example-generic" class="carousel slide" data-wrap="false">' +
901-
'<ol class="carousel-indicators">' +
902-
'<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>' +
903-
'<li data-target="#carousel-example-generic" data-slide-to="1"/>' +
904-
'<li data-target="#carousel-example-generic" data-slide-to="2"/>' +
905-
'</ol>' +
901+
'<div class="carousel-indicators">' +
902+
'<button data-target="#carousel-example-generic" data-slide-to="0" class="active" aria-current="true"></button>' +
903+
'<button data-target="#carousel-example-generic" data-slide-to="1"></button>' +
904+
'<button data-target="#carousel-example-generic" data-slide-to="2"></button>' +
905+
'</div>' +
906906
'<div class="carousel-inner">' +
907907
'<div class="carousel-item active" id="one">' +
908908
'<div class="carousel-caption"></div>' +

js/tests/visual/carousel.html

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ <h1>Carousel <small>Bootstrap Visual Test</small></h1>
1818
<p>The transition duration should be around 2s. Also, the carousel shouldn't slide when its window/tab is hidden. Check the console log.</p>
1919

2020
<div id="carousel-example-generic" class="carousel slide" data-ride="carousel">
21-
<ol class="carousel-indicators">
22-
<li data-target="#carousel-example-generic" data-slide-to="0" class="active"></li>
23-
<li data-target="#carousel-example-generic" data-slide-to="1"></li>
24-
<li data-target="#carousel-example-generic" data-slide-to="2"></li>
25-
</ol>
21+
<div class="carousel-indicators">
22+
<button data-target="#carousel-example-generic" data-slide-to="0" class="active" aria-current="true"></button>
23+
<button data-target="#carousel-example-generic" data-slide-to="1"></button>
24+
<button data-target="#carousel-example-generic" data-slide-to="2"></button>
25+
</div>
2626
<div class="carousel-inner">
2727
<div class="carousel-item active">
2828
<img src="https://i.imgur.com/iEZgY7Y.jpg" alt="First slide">

scss/_carousel.scss

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,10 @@
140140
}
141141

142142

143-
// Optional indicator pips
143+
// Optional indicator pips/controls
144144
//
145-
// Add an ordered list with the following class and add a list item for each
146-
// slide your carousel holds.
145+
// Add an container (such as a list) with the following class and add an item (ideally a focusable control,
146+
// like a button) with data-target for each slide your carousel holds.
147147

148148
.carousel-indicators {
149149
position: absolute;
@@ -153,23 +153,26 @@
153153
z-index: 15;
154154
display: flex;
155155
justify-content: center;
156-
padding-left: 0; // override <ol> default
156+
padding: 0;
157157
// Use the .carousel-control's width as margin so we don't overlay those
158158
margin-right: $carousel-control-width;
159+
margin-bottom: 1rem;
159160
margin-left: $carousel-control-width;
160161
list-style: none;
161162

162-
li {
163+
[data-target] {
163164
box-sizing: content-box;
164165
flex: 0 1 auto;
165166
width: $carousel-indicator-width;
166167
height: $carousel-indicator-height;
168+
padding: 0;
167169
margin-right: $carousel-indicator-spacer;
168170
margin-left: $carousel-indicator-spacer;
169171
text-indent: -999px;
170172
cursor: pointer;
171173
background-color: $carousel-indicator-active-bg;
172174
background-clip: padding-box;
175+
border: 0;
173176
// Use transparent borders to increase the hit area by 10px on top and bottom.
174177
border-top: $carousel-indicator-hit-area-height solid transparent;
175178
border-bottom: $carousel-indicator-hit-area-height solid transparent;

site/content/docs/4.6/components/carousel.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,11 @@ You can also add the indicators to the carousel, alongside the controls, too.
8080

8181
{{< example >}}
8282
<div id="carouselExampleIndicators" class="carousel slide" data-ride="carousel">
83-
<ol class="carousel-indicators">
84-
<li data-target="#carouselExampleIndicators" data-slide-to="0" class="active"></li>
85-
<li data-target="#carouselExampleIndicators" data-slide-to="1"></li>
86-
<li data-target="#carouselExampleIndicators" data-slide-to="2"></li>
87-
</ol>
83+
<div class="carousel-indicators">
84+
<button type="button" data-target="#carouselExampleIndicators" data-slide-to="0" class="active" aria-current="true" aria-label="Slide 1"></button>
85+
<button type="button" data-target="#carouselExampleIndicators" data-slide-to="1" aria-label="Slide 2"></button>
86+
<button type="button" data-target="#carouselExampleIndicators" data-slide-to="2" aria-label="Slide 3"></button>
87+
</div>
8888
<div class="carousel-inner">
8989
<div class="carousel-item active">
9090
{{< placeholder width="800" height="400" class="bd-placeholder-img-lg d-block w-100" color="#555" background="#777" text="First slide" >}}
@@ -113,11 +113,11 @@ Add captions to your slides easily with the `.carousel-caption` element within a
113113

114114
{{< example >}}
115115
<div id="carouselExampleCaptions" class="carousel slide" data-ride="carousel">
116-
<ol class="carousel-indicators">
117-
<li data-target="#carouselExampleCaptions" data-slide-to="0" class="active"></li>
118-
<li data-target="#carouselExampleCaptions" data-slide-to="1"></li>
119-
<li data-target="#carouselExampleCaptions" data-slide-to="2"></li>
120-
</ol>
116+
<div class="carousel-indicators">
117+
<button type="button" data-target="#carouselExampleCaptions" data-slide-to="0" class="active" aria-current="true" aria-label="Slide 1"></button>
118+
<button type="button" data-target="#carouselExampleCaptions" data-slide-to="1" aria-label="Slide 2"></button>
119+
<button type="button" data-target="#carouselExampleCaptions" data-slide-to="2" aria-label="Slide 3"></button>
120+
</div>
121121
<div class="carousel-inner">
122122
<div class="carousel-item active">
123123
{{< placeholder width="800" height="400" class="bd-placeholder-img-lg d-block w-100" color="#555" background="#777" text="First slide" >}}

site/content/docs/4.6/examples/carousel/index.html

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@
3434
<main role="main">
3535

3636
<div id="myCarousel" class="carousel slide" data-ride="carousel">
37-
<ol class="carousel-indicators">
38-
<li data-target="#myCarousel" data-slide-to="0" class="active"></li>
39-
<li data-target="#myCarousel" data-slide-to="1"></li>
40-
<li data-target="#myCarousel" data-slide-to="2"></li>
41-
</ol>
37+
<div class="carousel-indicators">
38+
<button type="button" data-target="#myCarousel" data-slide-to="0" class="active" aria-current="true" aria-label="Slide 1"></button>
39+
<button type="button" data-target="#myCarousel" data-slide-to="1" aria-label="Slide 2"></button>
40+
<button type="button" data-target="#myCarousel" data-slide-to="2" aria-label="Slide 3"></button>
41+
</div>
4242
<div class="carousel-inner">
4343
<div class="carousel-item active">
4444
{{< placeholder width="100%" height="100%" background="#777" color="#777" text=" " title=" " >}}

0 commit comments

Comments
 (0)