Home > other >  Horizontal scroll areas with buttons and gradients
Horizontal scroll areas with buttons and gradients

Time:04-05

This is my code so far:

// Show and hide gradients

$(document).ready(function() {
  $(".scroll-area").each(function(index) {
    if ($(this)[0].scrollWidth <= $(this)[0].clientWidth) {
      $(this).closest(".container").find(".left").css("display", "none");
      $(this).closest(".container").find(".right").css("display", "none");
    } else {
      $(this).scroll(function() {
        if ($(this)[0].scrollWidth > $(this)[0].clientWidth) {
          if ($(this).scrollLeft() > 0) {
            $(this).closest(".container").find(".left").css("display", "block");
          }

          if ($(this).scrollLeft() == 0) {
            $(this).closest(".container").find(".left").css("display", "none");
          }

          var fullWidth = $(this)[0].scrollWidth - $(this)[0].offsetWidth - 1;

          if ($(this).scrollLeft() >= fullWidth) {
            $(this).closest(".container").find(".right").css("display", "none");
          }

          if ($(this).scrollLeft() < fullWidth) {
            $(this).closest(".container").find(".right").css("display", "block");
          }
        }
      });
    }
  });
});


// Scroll buttons

let interval;

$('.scroll-btn').on('mousedown', ({
  target
}) => {
  const type = $(target).attr('id');

  interval = setInterval(() => {
    var x = $('#a').scrollLeft();
    $('#a').scrollLeft(type === 'left-arrow' ? x - 10 : x   10);
  }, 50);
});

$('.scroll-btn').on('mouseup', () => clearInterval(interval));
* {
  margin: 0;
  padding: 0;
  font-family: sans-serif;
  font-size: 16px;
}

.container {
  width: 550px;
  height: 80px;
  background-color: grey;
  position: relative;
  margin-bottom: 20px;
}

.scroll-area {
  white-space: nowrap;
  overflow-x: auto;
  height: 100%;
}

.left,
.right {
  width: 50px;
  height: 100%;
  position: absolute;
  pointer-events: none;
  top: 0;
}

.left {
  background: linear-gradient(90deg, orange 0%, rgba(0, 0, 0, 0) 100%);
  left: 0;
  display: none;
}

.right {
  background: linear-gradient(-90deg, orange 0%, rgba(0, 0, 0, 0) 100%);
  right: 0;
}

.arrow {
  display: block;
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  width: 15px;
  cursor: pointer;
}

.left-arrow {
  left: 0;
}

.right-arrow {
  right: 0;
}

.left-arrow div,
.right-arrow div {
  font-size: 40px;
}
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>

<div >

  <div id="x" ></div>
  <div ></div>

  <div >
    <div  id="left-arrow">
      <</div>
    </div>
    <div >
      <div  id="right-arrow">></div>
    </div>

    <div id="a" >
      <div >Scroll to right. The gradients and arrows should appear and disappear based on the scroll position. It should work with more than one container. Lorem ipsum.</div>
    </div>
  </div>

The needs are:

  1. The arrows should appear and disappear in the same way like the gradients.
  2. If there is not enough text to cause a scrollable area, there should be no gradient and now arrow.
  3. There should be more than one container in the end.

Can somebody help me to do that? I would be super thankful!

CodePudding user response:

Some things about your code:

  1. Your original code would not work with multiple containers, because you had a hardcoded #a ID in the interval code. You should really only have IDs on one element ideally, anyways (they're unique identifiers, while classes can be placed on multiple elements). The .scroll-area element should be found based on the target clicked.
  2. You should combine your gradient and arrow elements into one element. By that, I mean making the div in which the arrow lives should be a child of the gradient div. Why manage them both separately?
  3. Use class adding/removing/toggling instead of directly setting the CSS. Remember - when you find yourself writing the same code multiple times, it usually means there is a way to condense it down and make your code more dry and easier to understand read.
  4. Don't use the literal < and > symbols, as it can confuse some browsers. Use &lt; and &gt; instead.
  5. Rather than toggling display to none and block, it's better to use visibility in this specific case. In my example, we use opacity for a fun fading effect.
  6. Don't forget to listen for both mouseup mouseout events :)

Here is the working solution. I've refactored the code a bit:

let interval;

$('.arrow').on('mousedown', ({ target }) => {
    const type = target.classList[1];

    const scrollArea = $(target).parent().find('.scroll-area');
    interval = setInterval(() => {
        const prev = scrollArea.scrollLeft();
        scrollArea.scrollLeft(type === 'left-arrow' ? prev - 10 : prev   10);
    }, 50);
});

$('.arrow').on('mouseup mouseout', () => clearInterval(interval));

$('.scroll-area').on('scroll', ({ target }) => {
    const left = $(target).parent().find('.left-arrow');
    const right = $(target).parent().find('.right-arrow');

    const scroll = $(target).scrollLeft();

    const fullWidth = $(target)[0].scrollWidth - $(target)[0].offsetWidth;

    if (scroll === 0) left.addClass('hide');
    else left.removeClass('hide');

    if (scroll > fullWidth) right.addClass('hide');
    else right.removeClass('hide');
});
.container {
    width: 550px;
    height: 80px;
    background: grey;
    position: relative;
}

.right-arrow,
.left-arrow {
    height: 100%;
    width: 50px;
    position: absolute;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 2rem;
    cursor: pointer;
    transition: all 0.2s linear;
}

.scroll-area {
    white-space: nowrap;
    overflow-x: scroll;
    height: 100%;
}

.right-arrow {
    background: linear-gradient(-90deg, orange 0%, rgba(0, 0, 0, 0) 100%);
    left: 500px;
}

.left-arrow {
    background: linear-gradient(90deg, orange 0%, rgba(0, 0, 0, 0) 100%);
    left: 0px;
}

.scroll-btn {
    pointer-events: none;
}

.hide {
    opacity: 0;
}
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>

<div >
    <div >
        <div  id="left-arrow">&lt;</div>
    </div>

    <div >
        <div  id="right-arrow">&gt;</div>
    </div>

    <div >
        <div >
            Scroll to right. The gradients and arrows should appear and disappear based on the scroll position. It should work with more than one
            container. Lorem ipsum.
        </div>
    </div>
</div>

PS: If you don't like the fade effect, remove the transition: all 0.2s linear; part of the CSS, and switch .hide's opacity: 0 to visibility: hidden.

CodePudding user response:

You can put your arrows inside the left/right gradient divs. That way they will show/hide same way as the gradients.

EDIT

I cleaned up the code a bit since the original answer was kinda messy. (or 'weird' as mstephen19 put it :)).

// Show gradient and left/right arrows only if scrollable
$(".scroll-area").each((i, el) => {
  $(el).parent().find(".right")[el.scrollWidth > el.clientWidth ? "show" : "hide"]();
});

// Show/hide gradient and arrows on scroll
$('.scroll-area').scroll((e) => {
  const fullWidth = $(e.target)[0].scrollWidth - $(e.target)[0].offsetWidth - 1;
  const left = $(e.target).scrollLeft()

  $(e.target).parent().find(".left, .left-arrow")[left > 0 ? "show" : "hide"]();
  $(e.target).parent().find(".right, .right-arrow")[left < fullWidth ? "show" : "hide"]();
});

// Scroll on left/right arrow mouse down
let intervalId;
$(".left-arrow, .right-arrow").on("mousedown", (e) => {
  const scroll = $(e.target).closest(".container").find(".scroll-area");
  intervalId = setInterval(() => {
    const left = scroll.scrollLeft();
    scroll.scrollLeft(e.target.classList.contains("left-arrow") ? left - 10 : left   10);
  }, 50);
}).on("mouseup mouseleave", () => {
  clearInterval(intervalId);
});
* {
  margin: 0;
  padding: 0;
  font-family: sans-serif;
  font-size: 16px;
}

.container {
  width: 550px;
  height: 80px;
  background-color: grey;
  position: relative;
  margin-bottom: 20px;
  margin-left: 20px;
}

.scroll-area {
  white-space: nowrap;
  overflow-x: auto;
  height: 100%;
}

.left,
.right {
  width: 50px;
  height: 100%;
  position: absolute;
  top: 0;
}

.left {
  background: linear-gradient(90deg, orange 0%, rgba(0, 0, 0, 0) 100%);
  left: 0;
  display: none;
}

.right {
  background: linear-gradient(-90deg, orange 0%, rgba(0, 0, 0, 0) 100%);
  right: 0;
  text-align: right;
}

.left-arrow,
.right-arrow {
  margin: 0 10px;
  position: absolute;
  top: 50%;
  -ms-transform: translateY(-50%);
  transform: translateY(-50%);
  cursor: pointer;
  user-select: none;
  font-size: 40px
}

.left-arrow {
  display: none;
  left: -25px;
}

.right-arrow {
  right: -25px;
}
<html>

<head>
  <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>

<body>
  <div >
    <div ></div>
    <div ></div>
    
    <div >&lt;</div>
    <div >&gt;</div>

    <div >
      <div >Scroll to right. The gradients and arrows should appear and disappear based on the scroll position. It should work with more than one container. Lorem ipsum.</div>
    </div>
  </div>
  
  <div >
    <div ><span >&lt;</span></div>
    <div ><span >&gt;</span></div>

    <div >
      <div >No scroll.</div>
    </div>
  </div>

</body>

</html>

  • Related