Scroll element into view within container, but not in browser window
Asked Answered
D

1

11

I have a container with tags that line up horizontally. See example below.

The container has overflow:hidden and white-space:nowrap, so the tags can potentially go out to the right forever. I'd like to programatically horizontal scroll to a given tag, provided I have its HTML element in Javascript.

In my test component, I'd been using el.scrollIntoView(), which seemed to be working perfectly. But then I integrated the component into a larger HTML page that scrolled vertically, and I noticed each time I called el.scrollIntoView, it would still scroll into view horizontally within its container, but the whole page would also jump to where the container was right in line with the top of the browser window. It's this second behavior that I obviously don't want.

Is there some way to scroll into view only within a container, and not have the browser window also jump? I'd like to use vanilla JS, if possible.

function scrollToLast() {
  var container = document.getElementById('container');
  var tags = container.getElementsByTagName('span');
  tags[tags.length-1].scrollIntoView()
}
body {
  text-align: center;
}

#container {
  border: 1px solid;
  margin: 100px auto 20px;
  overflow: hidden;
  padding: 10px;
  text-align: left;
  white-space: nowrap;
  width: 75%
}

#container > span {
  border: 1px solid grey;
  display: inline-block;
  margin-right: 5px;
  padding: 3px;
}

button {
  margin-bottom: 100px;
}

.extra-content-on-page:before {
  content: 'Simulates more content';
}

.extra-content-on-page {
  background: #f2f2f2;
  height: 100px;
}
<div class="extra-content-on-page"></div>

<div id="container">
  <span>Tag 1</span>
  <span>Tag 2</span>
  <span>Tag 3</span>
  <span>Tag 4</span>
  <span>Tag 5</span>
  <span>Tag 6</span>
  <span>Tag 7</span>
  <span>Tag 8</span>
  <span>Tag 9</span>
  <span>Tag 10</span>
  <span>Tag 11</span>
  <span>Tag 12</span>
  <span>Tag 13</span>
  <span>Tag 14</span>
  <span>Tag 15</span>
  <span>Tag 16</span>
  <span>Tag 17</span>
  <span>Tag 18</span>
  <span>Tag 19</span>
  <span>Tag 20</span>
  <span>Tag 21</span>
  <span>Tag 22</span>
  <span>Tag 23</span>
  <span>Tag 24</span>
  <span>Tag 25</span>
  <span>Tag 26</span>
  <span>Tag 27</span>
  <span>Tag 28</span>
  <span>Tag 29</span>
  <span>Tag 30</span>
  <span>Tag 31</span>
</div>

<button onClick="scrollToLast()">Scroll to Tag 31</button>

<div class="extra-content-on-page"></div>
<div class="extra-content-on-page"></div>
<div class="extra-content-on-page"></div>
<div class="extra-content-on-page"></div>
<div class="extra-content-on-page"></div>
<div class="extra-content-on-page"></div>
<div class="extra-content-on-page"></div>
<div class="extra-content-on-page"></div>
Dumont answered 12/12, 2018 at 21:10 Comment(0)
Y
6

Consider changing container.scrollLeft value:

function scrollToLast() {
  var container = document.getElementById('container');
  var tags = container.getElementsByTagName('span');
  container.scrollLeft = tags[tags.length-1].offsetLeft;
}
body {
  text-align: center;
}

#container {
  border: 1px solid;
  margin: 100px auto 20px;
  overflow: hidden;
  padding: 10px;
  text-align: left;
  white-space: nowrap;
  width: 75%;
  scroll-behavior: smooth;
}

#container > span {
  border: 1px solid grey;
  display: inline-block;
  margin-right: 5px;
  padding: 3px;
}

button {
  margin-bottom: 100px;
}

.extra-content-on-page:before {
  content: 'Simulates more content';
}

.extra-content-on-page {
  background: #f2f2f2;
  height: 100px;
}
<div class="extra-content-on-page"></div>

<div id="container">
  <span>Tag 1</span>
  <span>Tag 2</span>
  <span>Tag 3</span>
  <span>Tag 4</span>
  <span>Tag 5</span>
  <span>Tag 6</span>
  <span>Tag 7</span>
  <span>Tag 8</span>
  <span>Tag 9</span>
  <span>Tag 10</span>
  <span>Tag 11</span>
  <span>Tag 12</span>
  <span>Tag 13</span>
  <span>Tag 14</span>
  <span>Tag 15</span>
  <span>Tag 16</span>
  <span>Tag 17</span>
  <span>Tag 18</span>
  <span>Tag 19</span>
  <span>Tag 20</span>
  <span>Tag 21</span>
  <span>Tag 22</span>
  <span>Tag 23</span>
  <span>Tag 24</span>
  <span>Tag 25</span>
  <span>Tag 26</span>
  <span>Tag 27</span>
  <span>Tag 28</span>
  <span>Tag 29</span>
  <span>Tag 30</span>
  <span>Tag 31</span>
</div>

<button onClick="scrollToLast()">Scroll to Tag 31</button>

<div class="extra-content-on-page"></div>
<div class="extra-content-on-page"></div>
<div class="extra-content-on-page"></div>
<div class="extra-content-on-page"></div>
<div class="extra-content-on-page"></div>
<div class="extra-content-on-page"></div>
<div class="extra-content-on-page"></div>
<div class="extra-content-on-page"></div>
Yorke answered 12/12, 2018 at 21:19 Comment(2)
Thanks. This is certainly reasonable, but it means the scrolled to tag is always on the very left most part of the container. I'd hoped to allow keyboard navigation through the tags in almost a "page-by-page" fashion, which scrollIntoView() allowed me to do to some extent, by using the "center" value for the "inline" option you can provide to it, per MDN. To get the same behavior with scrollLeft, I'm just not sure how I'd do it.Dumont
@MegaMatt, you might use container.offsetWidth and tag[i].offsetWidth to adjust the scrolling position. E.g. container.scrollLeft=tags[i].offsetLeft+(container.offsetWidth-tags[i].offsetWidth)/2; would center the tag[i] within the container.Yorke

© 2022 - 2024 — McMap. All rights reserved.