Slide to Paginate

Slide to Paginate

An underrated issue with web pagination is the amount of accessible pages: when dozens are available, the selector becomes too large (and unusable on mobile) or incomplete (as not all pages are rappresented and selectable; sometime, just the previous and the next pages are available).

So I tried to replace the classic pagination component with an interactive input range, which fits any width and contains any number of steps. Moving the slider, a tooltip suggests the page to jump to (useful on mobile interaction); releasing the slider, if the selected index differs the current page, a reload is triggered.

In the Blade template, the range pagination is used when the number of pages is more the 10:

@if($pages >= 10)
    <nav>
        <div id="range-paginator-wrap">
            <div class="range-value" id="range-paginator-label"></div>
            <input id="range-paginator" type="range" min="1" max="{{ $pages }}" value="{{ $current_page + 1 }}" step="1" data-url="{{ $url . '?page=XXX' }}" data-range-message="Go to page XXX">
        </div>
    </nav>
@else
    <nav>
        <!-- Here comes the classic pagination -->
    </nav>
@endif

The Javascript:

let rangepaginator = $('#range-paginator');
if (rangepaginator.length) {
	let current = parseInt(rangepaginator.val());

	rangepaginator.on('input', function(e) {
        /*
            Most of this code comes from
            https://codepen.io/onyx1812/pen/GRJxmva
        */
		let val = parseInt($(this).val());
		let min = parseInt($(this).attr('min'));
		let max = parseInt($(this).attr('max'));
		let newValue = Number((val - min) * 100 / (max - min) );
		let newPosition = 10 - (newValue * 0.2);

		let message = val;
		if (val != current) {
			message = $(this).attr('data-range-message').replace('XXX', val);
		}

		$('#range-paginator-label').html('<span>' + message + '</span>').css('left', 'calc(' + newValue + '% + (' + newPosition + 'px))');
	})
	.on('touchend mouseup', function(e) {
		let val = parseInt($(this).val());
		if (val != current) {
			let url = $(this).attr('data-url').replace('XXX', val - 1);
			window.location = url;
		}
		else {
            /*
                If the current page index is still selected,
                the tooltip disappears and nothing happens
            */
			$('#range-paginator-label').html('');
		}
	})
}

The Sass style (mostly inspired from this post):

#range-paginator-wrap {
    width: 100%;
    position: relative;

    #range-paginator {
        margin: 20px 0;
        width: 100%;

        &:focus {
            outline: none;
        }
    }

    #range-paginator-label {
        position: absolute;
        top: -50%;

        span {
            width: 100px;
            height: 24px;
            line-height: 24px;
            text-align: center;
            background: #03a9f4;
            color: #fff;
            font-size: 12px;
            display: block;
            position: absolute;
            left: 50%;
            transform: translate(-50%, 0);
            border-radius: 6px;

            &:before{
                content: "";
                position: absolute;
                width: 0;
                height: 0;
                border-top: 10px solid #03a9f4;
                border-left: 5px solid transparent;
                border-right: 5px solid transparent;
                top: 100%;
                left: 50%;
                margin-left: -5px;
                margin-top: -1px;
            }
        }
    }
}