Add prev/next buttons to scroll container with CSS and jQuery - javascript

After a long research I found a solution to add a custom scrollbar to a DIV element.
It's called SimpleBar. Docs can be found here.
The HTML structure and JS code is pretty simple:
Working demo
<div class="gallery" data-simplebar data-simplebar-auto-hide="false">
<div class="item"><img src="https://via.placeholder.com/250x150/0000FF" /></div>
<div class="item"><img src="https://via.placeholder.com/350x150/FF0000" /></div>
[...]
</div>
With data-simplebar I can add a custom scrollbar to any DIV.
There is just one thing I couldn't solve.
I want to add prev/next arrows to the scroll element.
The buttons should jump to the prev/next element in the DIV which is next to the left/right side of the div and not yet scrolled (partially) over.
And the JS should work for every slider instance on the site. Like the SimpleBar itself. There is no need for extra code per scroll container.
Is there anything I could use in jQuery?
EDIT: I found this answer and fiddle. I added the code to my example and changed it to left/right. It's not exactly what I need but I thought it could be a starting point. Unfortunately it doesn't work properly.

You can use the following code. Note that the scrolling depends on the viewport, so that once there's no more right width to go to, it won't have more room to move.
const DIRECTION = { PREV: 1, NEXT: 2 };
$(document).ready(function () {
$('.container').each(function (index, containerItem) {
var $gallery = $(containerItem).find('.gallery');
const simpleBar = new SimpleBar($gallery[0], {
autoHide: false,
scrollbarMaxSize: 50
});
var $scroller = $(simpleBar.getScrollElement());
$(containerItem).find('.scrollLeft').on('click', function () {
scroll(DIRECTION.PREV, $scroller);
event.preventDefault(); // prevents anchor jump on click
});
$(containerItem).find('.scrollRight').on('click', function () {
scroll(DIRECTION.NEXT, $scroller);
event.preventDefault();
});
$scroller.scroll(function () {
setButtonsVisibility($scroller);
});
});
});
function scroll(direction, $scroller) {
var $active = $scroller.find('.active');
var $target = direction == DIRECTION.PREV ? $active.prev() : $active.next();
if ($target.length) {
$scroller.animate({
scrollLeft: $target[0].offsetLeft
}, 2000);
$active.removeClass('active');
$target.addClass('active');
}
}
function setButtonsVisibility($scroller) {
var scrollLeft = $scroller.scrollLeft();
isScrollerStart($scroller, scrollLeft);
isScrollerEnd($scroller, scrollLeft);
}
function isScrollerStart($scroller, scrollLeft, $button) {
var $prevButton = $scroller.closest('.container').find('.scrollLeft');
if (scrollLeft == 0) {
$prevButton.css('visibility', 'hidden');
} else {
$prevButton.css('visibility', 'visible');
}
}
function isScrollerEnd($scroller, scrollLeft) {
var $nextButton = $scroller.closest('.container').find('.scrollRight');
var scrollWidth = $scroller[0].scrollWidth; // entire width
var scrollerWidth = $scroller.outerWidth() // visible width
if (scrollLeft >= scrollWidth - scrollerWidth) {
$nextButton.css('visibility', 'hidden');
} else {
$nextButton.css('visibility', 'visible');
}
}
.container {margin: 0 auto 2rem; max-width: 960px;}
.gallery {padding-bottom: 2rem; margin-bottom: 2rem;}
.gallery .simplebar-content {display: flex;}
.gallery .item {margin-right: 1rem;}
.simplebar-scrollbar:before {background: red !important;}
.simplebar-track.simplebar-horizontal {background: #eee !important;;}
.buttons {display: flex; justify-content: space-between; width: 100%; margin-bottom: 2rem;}
.buttons a {padding: 0.5rem; background: #ddd; text-decoration: none; color: #000;}
.scrollLeft { visibility: hidden; }
<script src="https://unpkg.com/simplebar#latest/dist/simplebar.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/simplebar#latest/dist/simplebar.css">
<div class="container">
<h2>Slider</h2>
<div class="gallery">
<div class="item active"><img src="https://via.placeholder.com/150x30/110000" /></div>
<div class="item"><img src="https://via.placeholder.com/450x30/3300FF" /></div>
<div class="item"><img src="https://via.placeholder.com/350x30/992244" /></div>
<div class="item"><img src="https://via.placeholder.com/400x30/0000FF" /></div>
</div>
<div class="buttons">
<a class="scrollLeft" href="#"><strong>Prev</strong> (remove if first)</a>
<a class="scrollRight" href="#"><strong>Next</strong> (remove if last)</a>
</div>
</div>
<div class="container">
<h2>Slider</h2>
<div class="gallery">
<div class="item active"><img src="https://via.placeholder.com/150x30/110000" /></div>
<div class="item"><img src="https://via.placeholder.com/450x30/3300FF" /></div>
<div class="item"><img src="https://via.placeholder.com/350x30/992244" /></div>
<div class="item"><img src="https://via.placeholder.com/400x30/0000FF" /></div>
</div>
<div class="buttons">
<a class="scrollLeft" href="#"><strong>Prev</strong> (remove if first)</a>
<a class="scrollRight" href="#"><strong>Next</strong> (remove if last)</a>
</div>
</div>

Related

Draggabilly: dragged elements in wrong position

i want to create a page with two areas. On the left is an "asset"-area, on the right the "content"-area. I have multiple elements in the "asset"-area. If i drag an element, it should be droppable in the right area.
I'm using the "draggabilly" library for simplicity. If i drag and drop an element, a copy of the original element should be created. This is fine. But if i drop an element, the element doesn't appear on the position of the mouse. Instead, it appears on other positions on the page.
I've got the following code:
HTML
<h2>Assets</h2>
<div id="tool-box">
<!--<img src="https://via.placeholder.com/100x100" draggable="true" ondragstart="drag(event)">-->
<!--<div id="clone-container"></div>-->
<img src="https://via.placeholder.com/100x100" class="draggable">
<div class="draggable"></div>
<div class="draggable"></div>
<div class="draggable"></div>
<div class="draggable"></div>
</div>
</div>
<!-- Main Panel -->
<div>
<h2>Editor</h2>
<div id="box-content">
<!--<div id="test1" class="content" ondrop="drop(event)" ondragover="allowDrop(event)""></div>-->
<div class=" content">b</div>
<div class="content">c</div>
<div class="content">d</div>
</div>
JS
const draggableElems = document.querySelectorAll('.draggable');
const cloneContainer = document.querySelector("#box-content");
draggableElems.forEach(element => {
const draggable = new Draggabilly(element);
draggable.on('dragStart', function() {
const clone = element.cloneNode(true);
clone.classList.add('clone');
clone.style.position = "absolute";
clone.style.left = `${element.offsetLeft}px`;
clone.style.top = `${element.offsetTop}px`;
document.body.appendChild(clone);
draggable.element = clone;
const draggableClone = new Draggabilly(clone);
});
draggable.on('dragMove', function(event) {
draggable.element.style.left = `${pointer.pageX}px`;
draggable.element.style.top = `${pointer.pageY}px`;
});
CSS
body { font-family: sans-serif; }
.draggable {
width: 140px;
height: 140px;
background: #000FFF;
border-radius: 10px;
cursor: move;
}
.draggable.is-pointer-down {
background: #09F;
}
.draggable.is-dragging { opacity: 0.7; }
I tried several code for the position, but none works as expected.
Do you have any clue, why the elements appear on the wrong position and how to fix it?
Thanks!

Adding class names on hover based on conditions

I've created a tabbed module which works by getting content that is in the .content div (which is hidden) and displaying it in a empty div called .overview.
The idea behind this tabbed module is that, on hover (or when class active exists), the content on the right will change based on what header is being selected from the left. I.e. If I hover over a header named "Red", the .overview div on the right will spit out "red".
However, the issues I'm having are the following:
In the demo below, don't hover on any of the headers. The .overview div has no content - which is obviously not ideal. If .tabs has class .active, then I want its content displayed on the right. I have a counter running which changes class active every 5 seconds. I don't only want to show stuff on hover.
Having said the above, if I hover over another tabs div, I want the counter to stop - to prevent it from adding class active to another .tabs div (because the hovered on tabs is active.
Demo:
$(document).ready(function() {
// add class .active on li hover
$('.tabs').mouseenter(function() {
//$('.tabs').removeClass('active');
$(this).parents('.tabs').addClass('active');
});
// Change active tab every x seconds
$(function() {
var list = $(".tabs"),
currentActive = 0;
time = 5; // interval in seconds
setInterval(function() {
currentActive = (currentActive + 1) % list.length;
list.removeClass('active').eq(currentActive).addClass('active');
}, time * 1000);
});
})
var overview = $('.overview');
$('.tabs').each(function(i) {
var thisTab = $(this);
var thisContent = thisTab.find('.content').html();
// when class .active exists, change content in .overview
if ($('.tabs').hasClass('active')) {
overview.html(thisContent);
}
// on hover, change content in .overview
thisTab.on('mouseenter', function(e) {
thisTab.addClass('active');
overview.html(thisContent);
})
.on('mouseleave', function(e) {
thisTab.removeClass('active');
overview.html('');
});
});
.tabs.active {
background: none yellow;
}
.list {
flex-basis: 40%;
}
.list li {
list-style-type: none;
}
.overview {
flex-basis: 60%;
border: 1px solid blue;
}
.content {
display: none;
}
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="d-flex flex-row">
<div class="list">
<li class="tabs active">
<div class="header"><span>Header</span></div>
<div class="content">
<p>Content 1</p>
</div>
</li>
<li class="tabs">
<div class="header"><span>Header 2</span></div>
<div class="content">
<p>Content 2</p>
</div>
</li>
<li class="tabs">
<div class="header"><span>Header 3</span></div>
<div class="content">
<p>Content 3</p>
</div>
</li>
</div>
<div class="overview"> </div>
</div>
Edit:
I've managed to make some movement on issue 1. I've added:
if ($('.tabs').hasClass('active')) {
overview.html(thisContent);
}
Which now, without hover, displays content in .overview, however, the content doesn't change when another tab is .active (i.e. in the demo, don't hover over anything, wait and it just shows content 3 for all headers.
I would do the following (I have commented what I have changed)
$(document).ready(function() {
var list = $(".tabs"),
overview = $('.overview'),
autoInterval, // interval var
currentActive = 0; // make this global to this closure
overview.html(list.eq(0).find('.content').html()); // set overview content
startInterval(); // start interval straight away
// add class .active on li hover
list.mouseenter(function() {
var thisTab = $(this);
currentActive = list.index(this); // set current active
list.removeClass('active'); // remove active class
thisTab.addClass('active'); // add active class
clearInterval(autoInterval); // clear the interval whilst hovering
var thisContent = thisTab.find('.content').html(); // get content
overview.html(thisContent); // set overview content
});
list.mouseleave(function() {
startInterval(); // restart the interval on mouseleave
});
function startInterval() {
// Change active tab every x seconds
time = 5; // interval in seconds
autoInterval = setInterval(function() {
currentActive = (currentActive + 1) % list.length;
list.removeClass('active');
var currentTab = list.eq(currentActive);
currentTab.addClass('active');
overview.html(currentTab.find('.content').html()); // set overview content
}, time * 1000);
}
});
.tabs.active {
background: none yellow;
}
.list {
flex-basis: 40%;
}
.list li {
list-style-type: none;
}
.overview {
flex-basis: 60%;
border: 1px solid blue;
}
.content {
display: none;
}
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="d-flex flex-row">
<div class="list">
<li class="tabs active">
<div class="header"><span>Header</span></div>
<div class="content">
<p>Content 1</p>
</div>
</li>
<li class="tabs">
<div class="header"><span>Header 2</span></div>
<div class="content">
<p>Content 2</p>
</div>
</li>
<li class="tabs">
<div class="header"><span>Header 3</span></div>
<div class="content">
<p>Content 3</p>
</div>
</li>
</div>
<div class="overview"> </div>
</div>
As soon as you add the mouseenter event, you need to stop the interval, you have the method clearInterval to do so.

Add fade in/out effect to logos in header when they switch from one to the other on scroll down

I have logo1 in the header changing to logo2 when user scrolls down. However, I don't want the logos to instantly switch but rather the first logo gradually fade out as the second logo fades in. I have added .fadeIn(slow) and .fadeOut(slow) in various places in my js but it's had no effect. Hoping I can get some help with this.
I've updated my question with more code. I've had 2 answers but can't get either to work for me and no more responses. Hoping an edited question with more detail will get a bit more attention.
<header>
<div id="nav" class="navbar">
<div id="nav_left">
HOME
SERVICES
PORTFOLIO
</div>
<a href="index.html" id="logo" class="Claire_logo">
<img src="images/logo_6_small.png" alt="logo2" id="logo_Claire" class="logo_main"
style="display:none" />
<img src="images/logo_bluebird_90_cc.png" alt="logo1" id="logo_Claire_blue" class="logo" />
</a>
<div id="nav_right">
BLOG
ABOUT
GET IN TOUCH
</div>
</div>
</header>
$(document).ready(function() {
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll > 0) {
$(".navbar").addClass("navbar-scroll");
$(".logo").show();
} else {
$(".navbar").removeClass("navbar-scroll");
$(".logo").hide();
}
if (scroll > 120) {
// $(".navbar").addClass("nav-color");
$(".logo_main").show();
$(".logo").hide();
} else {
// $(".navbar").removeClass("nav-color");
$(".logo_main").hide();
$(".logo").show();
}
});
});
You can do that using jQuery replaceWith
$(document).ready(function() {
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll > 0) {
$('#oldlogo').fadeOut(500, function() {
$('#oldlogo').replaceWith('<div id="newlogo"><img src="newlogo" alt="newlogo"/></div>').fadeIn(500);
});
}
});
});
body {
height: 200vh
}
header {
height: 50px;
position: fixed;
top: 0;
width: 100%;
background: white;
}
#nav {
display: flex;
justify-content: center;
flex-direction: row;
height: 180px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<header>
<div id="nav" class="navbar">
<div id="oldlogo"> <img src="oldlogooldlogo" alt="oldlogo" /></div>
</div>
</header>
I edited my answer. I missed the error in your code. I commented below in codes. You can use both fadeIn & fadeOut or show & hide methods as well. But for fadeing effect using fadeIn & fadeOut maybe looks better than show & hide
$(document).ready(function() {
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll >= 0 && scroll <= 120) { // I missed this logic mistake for the first time.
$(".navbar").addClass("navbar-scroll");
$(".logo").fadeIn(300); // or show(300)
} else {
$(".navbar").removeClass("navbar-scroll");
$(".logo").fadeOut(0); // or hide()
}
if (scroll > 120) {
// $(".navbar").addClass("nav-color");
$(".logo_main").fadeIn(300);
$(".logo").fadeOut(0);
} else {
// $(".navbar").removeClass("nav-color");
$(".logo_main").fadeOut(0);
$(".logo").fadeIn(300);
}
});
});

Add a div below inline-block wrapped row - Part 2

A solution suggested by #musicnothing in an older thread displays a content div below the row of inline divs, this works good when the div.wrapblock is clicked itself.
http://jsfiddle.net/SYJaj/7/
function placeAfter($block) {
$block.after($('#content'));
}
$('.wrapblock').click(function() {
$('#content').css('display','inline-block');
var top = $(this).offset().top;
var $blocks = $(this).nextAll('.wrapblock');
if ($blocks.length == 0) {
placeAfter($(this));
return false;
}
$blocks.each(function(i, j) {
if($(this).offset().top != top) {
placeAfter($(this).prev('.wrapblock'));
return false;
} else if ((i + 1) == $blocks.length) {
placeAfter($(this));
return false;
}
});
});
The issue I'm having.
I need to trigger the same effect, but by adding the click event to a link within the wrapblock itself.
My code is nearly identical.
What I have changed is the click event handle, from $('.wrapblock').click(function() to $('.more').on('click', function() I also needed to add .closest(".wrapblock") for the content div to position itself outside of the wrapblock.
$('.more').on('click', function() {
...
if ($blocks.length == 0) {
placeAfter($(this).closest(".wrapblock"));
return false;
}
Everything can be seen and tested http://jsfiddle.net/7Lt1hnaL/
Would be great if somebody could shed some light on how I can calculate which block it needs to follow with the offset method, thanks in advance.
As you can see in the latest fiddle example, the content div is not displaying below the row of divs.
I also apologise, I wanted to post on the thread in discussion but I only have a minor posting reputation which doesn't let me, thanks.
var $chosen = null;
var $allBlocks = [];
$(function(){
$allBlocks = $('.wrapblock');
})
$(window).on('resize', function() {
if ($chosen != null) {
$('#content').css('display','none');
$('body').append($('#content'));
$chosen.trigger('click');
}
});
$('.more').on('click', function() {
$chosen = $(this);
var position = $chosen.parent('.wrapblock').position();
$('#content').css('display','inline-block');
$allBlocks.filter(function(idx, ele){
return $(ele).position().top == position.top;
})
.last()
.after($('#content'));
});
.wrapblock
{
background: #963a3a;
display: inline-block;
width: 90px;
height: 90px;
color: white;
font-size: 14px;
text-align: left;
padding: 10px;
margin: 10px;
vertical-align:top;
position:relative;
}
#content
{
display:none;
vertical-align:top;
width:100%;
background: #5582c1;
font-size: 12px;
color: white;
padding: 10px;
}
.more {
position:absolute;
bottom:15px;
right:15px;
cursor:pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<div class="wrapblock">1
<span class="more" data-ref="1">more</span>
</div>
<div class="wrapblock">2
<span class="more" data-ref="2">more</span>
</div>
<div class="wrapblock">3
<span class="more" data-ref="3">more</span>
</div>
<div class="wrapblock">4
<span class="more" data-ref="4">more</span>
</div>
<div class="wrapblock">5
<span class="more" data-ref="5">more</span>
</div>
<div class="wrapblock">6
<span class="more" data-ref="6">more</span>
</div>
<div class="wrapblock">7
<span class="more" data-ref="7">more</span>
</div>
<div class="wrapblock">8
<span class="more" data-ref="8">more</span>
</div>
<div class="wrapblock">9
<span class="more" data-ref="9">more</span>
</div>
<div id="content">Some Content</div>
Seems to do what you want. Basically, it just filters down the set of all blocks to the row of the block you clicked on using the assumption that they'll all have the same vertical offset (top), then takes the last one, because jQuery will keep them in document order, so that'll be the last one in the layout row.
Oh, and I updated the fiddle http://jsfiddle.net/7Lt1hnaL/1/

animation is not working as expected

I am trying an animation on the two divs on button click . Here is the demo i have created js fiddle. what i want is
when the user will click on the button the right div will slide to right (it will hide). and the width of left div will become 100%.
on second time when user will click the right div will visible from right to left slide and the width of left div will 50 %
I am trying this code .
my html is
<div class="col-md-12">
<div id="tags-left" class="col-md-6">
div left
</div>
</div>
<div id="tag-div" class="col-md-6">
div right
</div>
</div>
<div class="col-md-12">
<div class="btn-main">
<input id="show-tag" type="button" class="save-btn" value="Show Tag">
<input id="preview" type="button" class="save-btn" value="Preview">
</div>
my js is
$("#show-tag").click(function (e)
{
$( "#tag-div" ).toggle( "slow", function(element) {
//e.preventDefault();
if ($('#tag-div').is(":visible") ) {
$('#tags-left').css('width','50%');
} else {
$('#tags-left').css('width','100%');
}
});
});
$("#show-tag").click(function (e)
{
$( "#tag-div" ).toggle( "slow", function(element) {
//e.preventDefault();
if ($('#tag-div').is(":visible") ) {
$('#tags-left').css('width','50%');
} else {
$('#tags-left').css('width','100%');
}
});
});
.col-md-6 {
width:45%;
float:left;
background:red;
height:200px;
margin:3px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="col-md-12">
<div id="tags-left" class="col-md-6">
div left
</div>
</div>
<div id="tag-div" class="col-md-6">
div right
</div>
</div>
<div class="col-md-12">
<div class="btn-main">
<input id="show-tag" type="button" class="save-btn" value="Show Tag">
<input id="preview" type="button" class="save-btn" value="Preview">
</div>
This one is simple solution without doing much coding see the fiddle: https://jsfiddle.net/uzar3j4q/7/
JS
var action = 1;
$("#show-tag").click(function () {
if ( action == 1 ) {
$("#tag-div" ).animate({'width':'0%',});
$('#tags-left').animate({'width':'90%'});
action = 2;
} else {
$("#tag-div" ).animate({'width':'45%',});
$('#tags-left').animate({'width':'45%'});
action = 1;
}
});
CSS
.col-md-6 {
width:45%;
float:left;
background:red;
height:200px;
margin:3px;
overflow:hidden; /* This property is added just to hide overflowing content */
}
first of all .. put left and right div in same div and in css
CSS
.col-md-12 {
white-space: nowrap;
overflow: hidden;
height:200px;
}
and you can use animate() method in js
JS
$("#show-tag").click(function (e)
{
$( "#tag-div" ).toggle( "slow", function(element) {
//$('#tags-left').css('width','0%');
//e.preventDefault();
if ($('#tag-div').is(":visible") ) {
$('#tags-left').animate({'width':'45%'},500);
} else {
$('#tags-left').animate({'width':'100%'},500);
}
});
});
DEMO HERE
you can just play around that to get the exact action you need
Optimized #Nilesh Mahajan's answer.
Found a problem with it when clicking on the button continuously.
// Caching
var $tagsLeft = $('#tags-left'),
$tagDiv = $('#tag-div');
var tagLeftWidth,
tagDivWidth;
$("#show-tag").on('click', function () {
var $this = $(this);
$this.prop('disabled', true).addClass('disabled'); // Disable the button
tagLeftWidth = $tagDiv.width() ? '90%' : '45%';
tagDivWidth = $tagDiv.width() ? '0%' : '45%';
$tagDiv.animate({
'width': tagDivWidth
}, function() {
$this.prop('disabled', false).removeClass('disabled'); // Enabling button
});
$tagsLeft.animate({
'width': tagLeftWidth
});
});
Demo: https://jsfiddle.net/tusharj/uzar3j4q/11/
Try this html:
<div id="tag-left" class="col-md-6">div left</div>
<div id="tag-right" class="col-md-6">div right</div>
and this javascript:
$("#show-tag").click(function (e) {
if($("#tag-right").width() == 0) {
$("#tag-left").animate({
width: '0'
});
$("#tag-right").animate({
width: '90%'
});
} else {
$("#tag-left").animate({
width: '90%'
});
$("#tag-right").animate({
width: '0'
});
}
});
jsfiddle

Categories