Scroll container so that given element becomes visible

by Dmitry

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.js"></script>
<div class="scrollable">
<table>
<tbody>
<tr num="1"><td>Row 1</td></tr>
<tr num="2"><td>Row 2</td></tr>
<tr num="3"><td>Row 3</td></tr>
<tr num="4"><td>Row 4</td></tr>
<tr num="5"><td>Row 5</td></tr>
<tr num="6"><td>Row 6</td></tr>
<tr num="7"><td>Row 7</td></tr>
<tr num="8"><td>Row 8</td></tr>
<tr num="9"><td>Row 9</td></tr>
<tr num="10"><td>Row 10</td></tr>
<tr num="11"><td>Row 11</td></tr>
<tr num="12"><td>Row 12</td></tr>
<tr num="13"><td>Row 13</td></tr>
<tr num="14"><td>Row 14</td></tr>
<tr num="15"><td>Row 15</td></tr>
<tr num="16"><td>Row 16</td></tr>
<tr num="17"><td>Row 17</td></tr>
<tr num="18"><td>Row 18</td></tr>
<tr num="19"><td>Row 19</td></tr>
<tr num="20"><td>Row 20</td></tr>
<tr num="21"><td>Row 21</td></tr>
<tr num="22"><td>Row 22</td></tr>
<tr num="23"><td>Row 23</td></tr>
<tr num="24"><td>Row 24</td></tr>
<tr num="25"><td>Row 25</td></tr>
<tr num="26"><td>Row 26</td></tr>
<tr num="27"><td>Row 27</td></tr>
<tr num="28"><td>Row 28</td></tr>
<tr num="29"><td>Row 29</td></tr>
<tr num="30"><td>Row 30</td></tr>
<tr num="31"><td>Row 31</td></tr>
<tr num="32"><td>Row 32</td></tr>
<tr num="33"><td>Row 33</td></tr>
<tr num="34"><td>Row 34</td></tr>
<tr num="35"><td>Row 35</td></tr>
<tr num="36"><td>Row 36</td></tr>
<tr num="37"><td>Row 37</td></tr>
<tr num="38"><td>Row 38</td></tr>
<tr num="39"><td>Row 39</td></tr>
<tr num="40"><td>Row 40</td></tr>
<tr num="41"><td>Row 41</td></tr>
<tr num="42"><td>Row 42</td></tr>
<tr num="43"><td>Row 43</td></tr>
<tr num="44"><td>Row 44</td></tr>
<tr num="45"><td>Row 45</td></tr>
<tr num="46"><td>Row 46</td></tr>
<tr num="47"><td>Row 47</td></tr>
<tr num="48"><td>Row 48</td></tr>
<tr num="49"><td>Row 49</td></tr>
<tr num="50"><td>Row 50</td></tr>
<tr num="51"><td>Row 51</td></tr>
<tr num="52"><td>Row 52</td></tr>
<tr num="53"><td>Row 53</td></tr>
<tr num="54"><td>Row 54</td></tr>
<tr num="55"><td>Row 55</td></tr>
<tr...

CSS

.scrollable {
  overflow-x: auto;
  height: 30em;
  width: 10%;
}

table {
  width: 100%;
  border: 1px solid black;
  border-collapse: collapse;
}

tr:not(:first-child) {
  border-top: 1px solid blue;
}

.focused {
  background-color: #FFFFC8;
}

JavaScript

(function($) {
	/**
	 * Scroll container so that given element becomes visible. Features:
	 * <ol>
	 * <li>If element is already visible, then no action is taken.
	 * <li>If element is above view port, the viewport is scrolled upwards so that element becomes visible at the top.
	 * <li>If element is below view port, the viewport is scrolled downwards so that element becomes visible at the bottom.
	 * </ol>
	 *
	 * @param element
	 *			optional string (selector) or jQuery object that controls the scrolling of the element
	 * @param options
	 *			optional extra settings
	 * @param options.animationSpeed
	 *			if defined, then scrolling is animated; determines time in milliseconds after which the element should
	 *			be scrolled into viewport
	 * @param options.heightScale
	 *			double number from 0 to 1; when scrolling the element from bottom sometimes it is desirable to scroll
	 *			element close to the top; e.g. to scroll it to the center specify 0.5; to scroll it to the top specify 0
	 * @param options.complete
	 * 			function to be called after animation is completed; if there is no animation, the function is called straight away
	 */
	$.fn.scrollTo = function(element, options) {
		options = options || {};
		var elementTop = element.offset().top;
		var containerTop = this.offset().top;
		var newScrollTop = null;

		if (elementTop < containerTop) {
			// Scroll to the top:
			newScrollTop = Math.round(this.scrollTop() + elementTop - containerTop);
		} else {
			// Scroll to the bottom:
			var elementBottom = elementTop + element.outerHeight(true);
			var containerHeight = this.height();

			if (elementBottom > containerTop + containerHeight) {
				if (options.heightScale != null) {
					if (options.heightScale === 0) {
						// This will effectively turn the formulae below into "elementTop - containerTop":
						containerHeight = element.outerHeight(true);
					} else {
						containerHeight *= options.heightScale;
					}
				}

				newScrollTop =...