Skip to content

Instantly share code, notes, and snippets.

@diendv96
Forked from hsablonniere/README.md
Created March 24, 2017 13:18
Show Gist options
  • Save diendv96/8d4db1f5ea7516e76ef6f409898a83f3 to your computer and use it in GitHub Desktop.
Save diendv96/8d4db1f5ea7516e76ef6f409898a83f3 to your computer and use it in GitHub Desktop.
scrollIntoViewIfNeeded 4 everyone!!!

scrollIntoViewIfNeeded 4 everyone!!!

This gist provides a simple JavaScript implementation of the non-standard WebKit method scrollIntoViewIfNeeded that can be called on DOM elements.

Usage

Just use the code in index.js in your app or website. You can see usage in the test page test.html.

The parent element will only scroll if the element being called is out of the view. The boolean can force the element to be centered in the scrolling area.

Improvements

  • This code doesn't handle well cases where the element has position:absolute (and maybe other CSS modifications).
  • This code doesn't handle cases where the scroll area isn't the direct parent of the target element.

?/!

If you have any ideas to improve this code or if you think the WebKit implementation doesn't work like this, feel free to start a discussion...

Hubert SABLONNIÈRE @hsablonniere

if (!Element.prototype.scrollIntoViewIfNeeded) {
Element.prototype.scrollIntoViewIfNeeded = function (centerIfNeeded) {
centerIfNeeded = arguments.length === 0 ? true : !!centerIfNeeded;
var parent = this.parentNode,
parentComputedStyle = window.getComputedStyle(parent, null),
parentBorderTopWidth = parseInt(parentComputedStyle.getPropertyValue('border-top-width')),
parentBorderLeftWidth = parseInt(parentComputedStyle.getPropertyValue('border-left-width')),
overTop = this.offsetTop - parent.offsetTop < parent.scrollTop,
overBottom = (this.offsetTop - parent.offsetTop + this.clientHeight - parentBorderTopWidth) > (parent.scrollTop + parent.clientHeight),
overLeft = this.offsetLeft - parent.offsetLeft < parent.scrollLeft,
overRight = (this.offsetLeft - parent.offsetLeft + this.clientWidth - parentBorderLeftWidth) > (parent.scrollLeft + parent.clientWidth),
alignWithTop = overTop && !overBottom;
if ((overTop || overBottom) && centerIfNeeded) {
parent.scrollTop = this.offsetTop - parent.offsetTop - parent.clientHeight / 2 - parentBorderTopWidth + this.clientHeight / 2;
}
if ((overLeft || overRight) && centerIfNeeded) {
parent.scrollLeft = this.offsetLeft - parent.offsetLeft - parent.clientWidth / 2 - parentBorderLeftWidth + this.clientWidth / 2;
}
if ((overTop || overBottom || overLeft || overRight) && !centerIfNeeded) {
this.scrollIntoView(alignWithTop);
}
};
}
<!DOCTYPE html>
<title>scrollIntoViewIfNeeded test page</title>
<style type="text/css">
body {
font: 14px Arial;
}
#scroll-area {
border: 1px solid #AAA;
height: 7em;
margin: 0;
overflow: auto;
padding: 0;
width: 400px;
white-space: nowrap;
}
#scroll-area li {
background: #EEE;
border-radius: 5px;
display: inline-block;
list-style: none;
padding: 5px 10px;
}
#scroll-area li:nth-child(2n+1) {
background: #DDD;
text-align: right;
}
#scroll-area li.selected {
background: #BADA55;
}
</style>
<h1>scrollIntoViewIfNeeded test page</h1>
<ul id="scroll-area"><li class="selected">item #0</li><li>item #1</li><li>item #2</li><li>item #3</li><li>item #4</li><li>item #5</li><li>item #6</li><li>item #7</li><li>item #8</li><li>item #9</li><li>item #10</li><li>item #11</li><li>item #12</li><li>item #13</li><li>item #14</li><li>item #15</li><li>item #16</li><li>item #17</li><li>item #18</li><li>item #19</li><li>item #20</li><li>item #21</li><li>item #22</li><li>item #23</li><li>item #24</li><li>item #25</li><li>item #26</li><li>item #27</li><li>item #28</li><li>item #29</li><li>item #30</li><li>item #31</li><li>item #32</li><li>item #33</li><li>item #34</li><li>item #35</li><li>item #36</li><li>item #37</li><li>item #38</li><li>item #39</li><li>item #40</li><li>item #41</li><li>item #42</li><li>item #43</li><li>item #44</li><li>item #45</li><li>item #46</li><li>item #47</li><li>item #48</li><li>item #49</li><li>item #50</li><li>item #51</li><li>item #52</li><li>item #53</li><li>item #54</li><li>item #55</li><li>item #56</li><li>item #57</li><li>item #58</li><li>item #59</li><li>item #60</li><li>item #61</li><li>item #62</li><li>item #63</li><li>item #64</li><li>item #65</li><li>item #66</li><li>item #67</li><li>item #68</li><li>item #69</li><li>item #70</li><li>item #71</li><li>item #72</li><li>item #73</li><li>item #74</li><li>item #75</li><li>item #76</li><li>item #77</li><li>item #78</li><li>item #79</li><li>item #80</li><li>item #81</li><li>item #82</li><li>item #83</li><li>item #84</li><li>item #85</li><li>item #86</li><li>item #87</li><li>item #88</li><li>item #89</li><li>item #90</li><li>item #91</li><li>item #92</li><li>item #93</li><li>item #94</li><li>item #95</li><li>item #96</li><li>item #97</li><li>item #98</li><li>item #99</li></ul>
<div id="buttons-centerFalse">
<span><code>scrollIntoViewIfNeeded(false)</code> to item : </span>
<button data-item-idx="0">#0</button>
<button data-item-idx="11">#11</button>
<button data-item-idx="22">#22</button>
<button data-item-idx="24">#24</button>
<button data-item-idx="26">#26</button>
<button data-item-idx="33">#33</button>
<button data-item-idx="44">#44</button>
<button data-item-idx="55">#55</button>
<button data-item-idx="66">#66</button>
<button data-item-idx="77">#77</button>
<button data-item-idx="82">#82</button>
<button data-item-idx="84">#84</button>
<button data-item-idx="86">#86</button>
<button data-item-idx="99">#99</button>
</div>
<div id="buttons-centerTrue">
<span><code>scrollIntoViewIfNeeded(true)</code> to item : </span>
<button data-item-idx="0">#0</button>
<button data-item-idx="11">#11</button>
<button data-item-idx="22">#22</button>
<button data-item-idx="24">#24</button>
<button data-item-idx="26">#26</button>
<button data-item-idx="33">#33</button>
<button data-item-idx="44">#44</button>
<button data-item-idx="55">#55</button>
<button data-item-idx="66">#66</button>
<button data-item-idx="77">#77</button>
<button data-item-idx="82">#82</button>
<button data-item-idx="84">#84</button>
<button data-item-idx="86">#86</button>
<button data-item-idx="99">#99</button>
</div>
<div id="buttons-centerUndefined">
<span><code>scrollIntoViewIfNeeded(undefined)</code> to item : </span>
<button data-item-idx="0">#0</button>
<button data-item-idx="11">#11</button>
<button data-item-idx="22">#22</button>
<button data-item-idx="24">#24</button>
<button data-item-idx="26">#26</button>
<button data-item-idx="33">#33</button>
<button data-item-idx="44">#44</button>
<button data-item-idx="55">#55</button>
<button data-item-idx="66">#66</button>
<button data-item-idx="77">#77</button>
<button data-item-idx="82">#82</button>
<button data-item-idx="84">#84</button>
<button data-item-idx="86">#86</button>
<button data-item-idx="99">#99</button>
</div>
<div id="buttons-centerNoArgs">
<span><code>scrollIntoViewIfNeeded()</code> to item : </span>
<button data-item-idx="0">#0</button>
<button data-item-idx="11">#11</button>
<button data-item-idx="22">#22</button>
<button data-item-idx="24">#24</button>
<button data-item-idx="26">#26</button>
<button data-item-idx="33">#33</button>
<button data-item-idx="44">#44</button>
<button data-item-idx="55">#55</button>
<button data-item-idx="66">#66</button>
<button data-item-idx="77">#77</button>
<button data-item-idx="82">#82</button>
<button data-item-idx="84">#84</button>
<button data-item-idx="86">#86</button>
<button data-item-idx="99">#99</button>
</div>
<script src="index.js"></script>
<script>
(function () {
var scrollArea = document.getElementById('scroll-area'),
buttonsCenterFalse = document.getElementById('buttons-centerFalse'),
buttonsCenterTrue = document.getElementById('buttons-centerTrue'),
buttonsCenterUndefined = document.getElementById('buttons-centerUndefined'),
buttonsCenterNoArgs = document.getElementById('buttons-centerNoArgs'),
scrollIntoViewIfNeededToItemAndSelect;
scrollIntoViewIfNeededToItemAndSelect = function (itemIdx, centerIfNeeded) {
scrollArea.querySelector('.selected').className = '';
// Allow us to really have difference bewteen scrollIntoViewIfNeeded() and scrollIntoViewIfNeeded(undefined)
if (arguments.length === 1) {
scrollArea.children[itemIdx].scrollIntoViewIfNeeded();
} else {
scrollArea.children[itemIdx].scrollIntoViewIfNeeded(centerIfNeeded);
}
scrollArea.children[itemIdx].className = 'selected';
};
buttonsCenterFalse.addEventListener('click', function (e) {
if (e.target.nodeName === 'BUTTON') {
scrollIntoViewIfNeededToItemAndSelect(e.target.dataset.itemIdx, false);
}
}, false);
buttonsCenterTrue.addEventListener('click', function (e) {
if (e.target.nodeName === 'BUTTON') {
scrollIntoViewIfNeededToItemAndSelect(e.target.dataset.itemIdx, true);
}
}, false);
buttonsCenterUndefined.addEventListener('click', function (e) {
if (e.target.nodeName === 'BUTTON') {
scrollIntoViewIfNeededToItemAndSelect(e.target.dataset.itemIdx, undefined);
}
}, false);
buttonsCenterNoArgs.addEventListener('click', function (e) {
if (e.target.nodeName === 'BUTTON') {
scrollIntoViewIfNeededToItemAndSelect(e.target.dataset.itemIdx);
}
}, false);
})();
</script>
<!DOCTYPE html>
<title>scrollIntoViewIfNeeded test page</title>
<style type="text/css">
body {
font: 14px Arial;
}
#scroll-area {
border: 1px solid #AAA;
height: 7em;
margin: 0;
overflow: auto;
padding: 0;
}
#scroll-area li {
background: #EEE;
border-radius: 5px;
list-style: none;
padding: 5px 10px;
}
#scroll-area li:nth-child(2n+1) {
background: #DDD;
}
#scroll-area li.selected {
background: #BADA55;
}
</style>
<h1>scrollIntoViewIfNeeded test page</h1>
<ul id="scroll-area"><li class="selected">item #0</li><li>item #1</li><li>item #2</li><li>item #3</li><li>item #4</li><li>item #5</li><li>item #6</li><li>item #7</li><li>item #8</li><li>item #9</li><li>item #10</li><li>item #11</li><li>item #12</li><li>item #13</li><li>item #14</li><li>item #15</li><li>item #16</li><li>item #17</li><li>item #18</li><li>item #19</li><li>item #20</li><li>item #21</li><li>item #22</li><li>item #23</li><li>item #24</li><li>item #25</li><li>item #26</li><li>item #27</li><li>item #28</li><li>item #29</li><li>item #30</li><li>item #31</li><li>item #32</li><li>item #33</li><li>item #34</li><li>item #35</li><li>item #36</li><li>item #37</li><li>item #38</li><li>item #39</li><li>item #40</li><li>item #41</li><li>item #42</li><li>item #43</li><li>item #44</li><li>item #45</li><li>item #46</li><li>item #47</li><li>item #48</li><li>item #49</li><li>item #50</li><li>item #51</li><li>item #52</li><li>item #53</li><li>item #54</li><li>item #55</li><li>item #56</li><li>item #57</li><li>item #58</li><li>item #59</li><li>item #60</li><li>item #61</li><li>item #62</li><li>item #63</li><li>item #64</li><li>item #65</li><li>item #66</li><li>item #67</li><li>item #68</li><li>item #69</li><li>item #70</li><li>item #71</li><li>item #72</li><li>item #73</li><li>item #74</li><li>item #75</li><li>item #76</li><li>item #77</li><li>item #78</li><li>item #79</li><li>item #80</li><li>item #81</li><li>item #82</li><li>item #83</li><li>item #84</li><li>item #85</li><li>item #86</li><li>item #87</li><li>item #88</li><li>item #89</li><li>item #90</li><li>item #91</li><li>item #92</li><li>item #93</li><li>item #94</li><li>item #95</li><li>item #96</li><li>item #97</li><li>item #98</li><li>item #99</li></ul>
<div id="buttons-centerFalse">
<span><code>scrollIntoViewIfNeeded(false)</code> to item : </span>
<button data-item-idx="0">#0</button>
<button data-item-idx="11">#11</button>
<button data-item-idx="22">#22</button>
<button data-item-idx="24">#24</button>
<button data-item-idx="26">#26</button>
<button data-item-idx="33">#33</button>
<button data-item-idx="44">#44</button>
<button data-item-idx="55">#55</button>
<button data-item-idx="66">#66</button>
<button data-item-idx="77">#77</button>
<button data-item-idx="82">#82</button>
<button data-item-idx="84">#84</button>
<button data-item-idx="86">#86</button>
<button data-item-idx="99">#99</button>
</div>
<div id="buttons-centerTrue">
<span><code>scrollIntoViewIfNeeded(true)</code> to item : </span>
<button data-item-idx="0">#0</button>
<button data-item-idx="11">#11</button>
<button data-item-idx="22">#22</button>
<button data-item-idx="24">#24</button>
<button data-item-idx="26">#26</button>
<button data-item-idx="33">#33</button>
<button data-item-idx="44">#44</button>
<button data-item-idx="55">#55</button>
<button data-item-idx="66">#66</button>
<button data-item-idx="77">#77</button>
<button data-item-idx="82">#82</button>
<button data-item-idx="84">#84</button>
<button data-item-idx="86">#86</button>
<button data-item-idx="99">#99</button>
</div>
<div id="buttons-centerUndefined">
<span><code>scrollIntoViewIfNeeded(undefined)</code> to item : </span>
<button data-item-idx="0">#0</button>
<button data-item-idx="11">#11</button>
<button data-item-idx="22">#22</button>
<button data-item-idx="24">#24</button>
<button data-item-idx="26">#26</button>
<button data-item-idx="33">#33</button>
<button data-item-idx="44">#44</button>
<button data-item-idx="55">#55</button>
<button data-item-idx="66">#66</button>
<button data-item-idx="77">#77</button>
<button data-item-idx="82">#82</button>
<button data-item-idx="84">#84</button>
<button data-item-idx="86">#86</button>
<button data-item-idx="99">#99</button>
</div>
<div id="buttons-centerNoArgs">
<span><code>scrollIntoViewIfNeeded()</code> to item : </span>
<button data-item-idx="0">#0</button>
<button data-item-idx="11">#11</button>
<button data-item-idx="22">#22</button>
<button data-item-idx="24">#24</button>
<button data-item-idx="26">#26</button>
<button data-item-idx="33">#33</button>
<button data-item-idx="44">#44</button>
<button data-item-idx="55">#55</button>
<button data-item-idx="66">#66</button>
<button data-item-idx="77">#77</button>
<button data-item-idx="82">#82</button>
<button data-item-idx="84">#84</button>
<button data-item-idx="86">#86</button>
<button data-item-idx="99">#99</button>
</div>
<script src="index.js"></script>
<script>
(function () {
var scrollArea = document.getElementById('scroll-area'),
buttonsCenterFalse = document.getElementById('buttons-centerFalse'),
buttonsCenterTrue = document.getElementById('buttons-centerTrue'),
buttonsCenterUndefined = document.getElementById('buttons-centerUndefined'),
buttonsCenterNoArgs = document.getElementById('buttons-centerNoArgs'),
scrollIntoViewIfNeededToItemAndSelect;
scrollIntoViewIfNeededToItemAndSelect = function (itemIdx, centerIfNeeded) {
scrollArea.querySelector('.selected').className = '';
// Allow us to really have difference bewteen scrollIntoViewIfNeeded() and scrollIntoViewIfNeeded(undefined)
if (arguments.length === 1) {
scrollArea.children[itemIdx].scrollIntoViewIfNeeded();
} else {
scrollArea.children[itemIdx].scrollIntoViewIfNeeded(centerIfNeeded);
}
scrollArea.children[itemIdx].className = 'selected';
};
buttonsCenterFalse.addEventListener('click', function (e) {
if (e.target.nodeName === 'BUTTON') {
scrollIntoViewIfNeededToItemAndSelect(e.target.dataset.itemIdx, false);
}
}, false);
buttonsCenterTrue.addEventListener('click', function (e) {
if (e.target.nodeName === 'BUTTON') {
scrollIntoViewIfNeededToItemAndSelect(e.target.dataset.itemIdx, true);
}
}, false);
buttonsCenterUndefined.addEventListener('click', function (e) {
if (e.target.nodeName === 'BUTTON') {
scrollIntoViewIfNeededToItemAndSelect(e.target.dataset.itemIdx, undefined);
}
}, false);
buttonsCenterNoArgs.addEventListener('click', function (e) {
if (e.target.nodeName === 'BUTTON') {
scrollIntoViewIfNeededToItemAndSelect(e.target.dataset.itemIdx);
}
}, false);
})();
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment