Skip to content Skip to sidebar Skip to footer

Move To Specific Div Based On Button Click

I was trying to move the divs (here it's question number) based on the prev and next button. So that the selected question is always visible on screen. Here is the demo : http://js

Solution 1:

Using scrollLeft is a bit tricky. I did a small redo of your use-case based on positioning and then moving it based on left of the container. The tricky part is to reliably calculate the negative position when scrolled to the extreme right. Also, need to take into account the widths and margins.

Check the below snippet:

var $wrap = $("#numWrap"), $strip = $("#strip"), 
    $leftArrow = $(".wrapper > .arrows").first(), 
    wrapWidth = $wrap.width() + $leftArrow.width(), 
    margin = 10;
    
fill(20); select($(".numberItem").first());

$strip.on("click", ".numberItem", function() { select($(this)); });

function select($elem) {
    $(".numberItem").removeClass("selected");
    $elem.addClass("visited").addClass("selected");
    focus($elem[0]);    
}

function focus(elem) {
    var stripPos = $strip.position(), 
        numPos = $(elem).offset(), 
        elemWidth = $(elem).width() + margin, 
        numRight = numPos.left + elemWidth;
    
    if (numRight > wrapWidth) {
        $strip.css({"left": stripPos.left - elemWidth}); 
    }
    if (numPos.left < (margin + $leftArrow.width()))  {
        $strip.css({"left": stripPos.left + elemWidth}); 
    }
}

$(".wrapper").on("click", "a.arrow", function() {
    var stripPos = $strip.position();
    if (this.id == "lft") {
        $strip.css({"left": stripPos.left + (wrapWidth / 2)}); 
    } else {
        $strip.css({"left": stripPos.left - (wrapWidth / 2)});
    }
});

$(".controls").on("click", "a.arrow", function() {
    var $sel = $(".selected"), numPos, $sel, elemWidth; 
    	$elem = $sel.length > 0 ? $sel.first() : $(".numberItem").first();
    if (this.id == "lft") {
        $sel = $elem.prev().length > 0 ? $elem.prev() : $elem;
        select($sel); 
    } else {
        $sel = $elem.next().length > 0 ? $elem.next() : $elem;
        select($sel);
    }
    numPos = $sel.offset(); elemWidth = $sel.width() + margin;
    numRight = numPos.left + elemWidth;
    if (numPos.left > wrapWidth) {
        $strip.css({"left": -($sel.text()) * $sel.width() });
    }
    if (numRight < 0) {
        $strip.css({"left": +($sel.text()) * $sel.width() });
    }
});

function fill(num){
    for (var i = 1; i <= num; i++) {
        var $d = $("<a href='#' class='numberItem'>" + i + "</a>");
        $strip.append($d);
    }
}
* { box-sizing: border-box; padding: 0; margin: 0; font-family: sans-serif; }
div.wrapper { 
    background-color: #ddd; width: 100vw; height: 64px; 
    clear: both; overflow: hidden; margin-top: 16px;
}
div.arrows { 
    float: left; width: 10%; min-width: 24px; height: 64px; line-height: 64px;
    text-align: center; vertical-align: middle; overflow: hidden;
}
div.numWrap { 
    float: left; height: 64px; line-height: 64px;
    width: 80%; vertical-align: middle; 
    overflow: hidden; position: relative;
}
div.strip { 
    position: absolute; left: 0px;
    width: auto; white-space: nowrap; 
    transition: left 1s;
}
a.numberItem { 
    display: inline-block; text-align: center; margin: 0px 8px;
    background-color: #fff; border-radius: 50%; width: 48px; height: 48px; 
    font-size: 1.2em; line-height: 48px; text-decoration: none;
}
a.numberItem.visited { background-color: #fff; color: #000; border: 2px solid #01aebc; }
a.numberItem.selected { background-color: #01aebc; color: #fff; }
div.controls { clear: both; }
div.controls > div.arrows { width: auto; margin: 0 12px; }
a, a:focus, a:active, a:link, a:visited { 
    display: inline-block; 
    text-decoration: none; font-weight: 600; 
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
    <div class="arrows">
        <a id="lft" class="arrow" href="#">&#x3008;</a>
    </div>
    <div id="numWrap" class="numWrap">
        <div id="strip" class="strip"></div>
    </div>
    <div class="arrows">
        <a id="rgt" class="arrow" href="#">&#x3009;</a>
    </div>
</div>
<div class="controls">
    <div class="arrows">
        <a id="lft" class="arrow" href="#">&#x3008;&nbsp; Previous</a>
    </div>
    <div class="arrows">
        <a id="rgt" class="arrow" href="#">Next &nbsp;&#x3009;</a>
    </div>    
<div>

Explanation:

  1. Using absolute positioning on the number container, which is nested to get 100% width.

Markup:

<div class="wrapper">
    <div class="arrows"><a id="lft" class="arrow" href="#">&#x3008;</a></div>
    <div id="numWrap" class="numWrap">
        <div id="strip" class="strip"></div> <!-- nesting here -->
    </div>
    <div class="arrows"><a id="rgt" class="arrow" href="#">&#x3009;</a></div>
</div>

CSS:

div.wrapper { 
    background-color: #ddd; width: 100vw; height: 64px; 
    clear: both; overflow: hidden; margin-top: 16px;
}
div.arrows { 
    float: left; width: 10%; min-width: 24px; height: 64px; line-height: 64px;
    text-align: center; vertical-align: middle; overflow: hidden;
}
div.numWrap { 
    float: left; height: 64px; line-height: 64px;
    width: 80%; vertical-align: middle; 
    overflow: hidden; position: relative; /* relatively positioned */
}
div.strip { 
    position: absolute; left: 0px;        /* absolutely positioned */
    width: auto; white-space: nowrap; 
    transition: left 1s;                  /* instead of jquery animate */
}

With this structure, we can now use left to control the scrolling.

  1. For partially obscured numbers, try to gently focus-in (nudge into view) a number which is partially obscured. This can be done by checking the position relative to parent and adding the width/margin to it and also accounting for width of the left arrow (it might peep thru).

Javascript:

function focus(elem) {
    var stripPos = $strip.position(), 
        numPos = $(elem).offset(), 
        elemWidth = $(elem).width() + margin, 
        numRight = numPos.left + elemWidth;

    // if it is towards right side, nudge it back inside
    if (numRight > wrapWidth) {
        $strip.css({"left": stripPos.left - elemWidth}); 
    } 
    // if it is towards left side, nudge it back inside
    if (numPos.left < (margin + $leftArrow.width()))  {
        $strip.css({"left": stripPos.left + elemWidth}); 
    } 
}
  1. Once the user has scrolled the list too far and then tries to click on previous / next buttons to select a question, then we need to move the entire container upto the selected number. We can easily do this by multiplying the question number with element width and then changing the left in positive (if towards right) or in negative (if towards left).

Javascript:

// if left of element is more than the width of parent
if (numPos.left > wrapWidth) {
    $strip.css({"left": -($sel.text()) * $sel.width() });
}
// if right of element is less than 0 i.e. starting position
if (numRight < 0) {
    $strip.css({"left": +($sel.text()) * $sel.width() });
}

Here is a fiddle to play with: http://jsfiddle.net/abhitalks/aw166qhx/

You will need to further adapt it to your use-case, but you get the idea.


Post a Comment for "Move To Specific Div Based On Button Click"