document.querySelectorAll('[data-control]').forEach(item => {
item.addEventListener('click', event => {
event.preventDefault();
handleFilterItems(item);
});
});
function handleFilterItems(filterItem) {
if (filterItem.getAttribute('data-control') === '' || filterItem.getAttribute('data-control') === '.all') {
document.querySelectorAll('[data-control]').forEach(item => {
item.classList.remove('_active');
});
} else {
document.querySelector('[data-control=".all"]').classList.remove('_active');
}
if (filterItem.classList.contains('_active')) {
filterItem.classList.remove('_active');
// check if no other filter item is active
if (!document.querySelectorAll('[data-control]._active').length) {
document.querySelector('[data-control=".all"]').classList.add('_active');
}
} else {
filterItem.classList.add('_active');
}
handleFilterTeaser();
}
function handleFilterTeaser() {
document.querySelectorAll('.all').forEach(item => {
item.classList.add('_hidden');
});
if (document.querySelectorAll('[data-control]._active').length === 0) {
document.querySelectorAll('.all').forEach(item => {
item.classList.remove('_hidden');
});
} else {
document.querySelectorAll('[data-control]._active').forEach(item => {
let filterTag = item.getAttribute('data-control');
document.querySelectorAll(filterTag).forEach(tag => {
tag.classList.remove('_hidden');
});
});
}
}
ul,
li {
list-style: none;
display: inline-block;
}
button {
padding: 10px;
}
button:hover {
color: green;
}
button._active {
color: tomato;
}
.list>div {
background: grey;
margin-bottom: 10px;
color: #fff;
padding: 10px;
}
._hidden {
display: none;
}
.show-choice {
width: max-content;
border: 1px solid red;
padding: 10px;
margin: 0 0 20px;
}
._hidden {
display: none;
}
.show-choice {
width: max-content;
border: 1px solid red;
padding: 10px;
margin: 0 0 20px
}
<div>
<ul>
<li>
<button class="_active" data-control=".all">all</button>
</li>
<li>
<button data-control=".a">a</button>
</li>
<li>
<button data-control=".b">b</button>
</li>
<li>
<button data-control=".c">c</button>
</li>
</ul>
<div class="show-choice">
<div id="choice">
<span>a 1, b 2, a b 3, b 4, a 5, c 6</span>
</div>
</div>
</div>
<div class="list">
<div class="all a">a 1</div>
<div class="all b">b 2</div>
<div class="all a b">a b 3</div>
<div class="all b">b 4</div>
<div class="all a">a 5</div>
<div class="all c">c 6</div>
</div>
When the page loads, all elements are shown (the <div id="choice"> field also shows all the options) When we select the display of certain data in the "choise" field, only the selected one should be displayed; if the selection is canceled, everything returns to its original state.
https://codepen.io/slagrach/pen/YzjQMOw
The way to achieve this with minimal changes is to modify the function handleFilterTeaser() to collect the text values of all selected elements, and then join them into a string and assign them to the element with id choice.
function handleFilterTeaser() {
let values = [];
document.querySelectorAll('.all').forEach(item => {
item.classList.add('_hidden');
});
if (document.querySelectorAll('[data-control]._active').length === 0) {
document.querySelectorAll('.all').forEach(item => {
item.classList.remove('_hidden');
values.push(item.innerHTML);
});
} else {
document.querySelectorAll('[data-control]._active').forEach(item => {
let filterTag = item.getAttribute('data-control');
document.querySelectorAll(filterTag).forEach(tag => {
tag.classList.remove('_hidden');
values.push(tag.innerHTML);
});
});
}
document.querySelector('#choice').innerHTML = values.join(', ');
}
https://codepen.io/Urnix/pen/GRBEVGj
Update: The above implementation allows duplicate items in #choice element. I would recommend rewriting the code like this:
document.querySelectorAll('[data-control]').forEach((button, index) => {
button.addEventListener('click', event => {
event.preventDefault();
handleFilterItems(index);
});
});
function handleFilterItems(buttonIndex) {
// Array of filters
const filters = [...document.querySelectorAll('[data-control]')];
// Array of names of active filters
let activeFilterNames = filters
.filter(function (button) {
return button.classList.contains('_active');
})
.map(function (button) {
return button.innerHTML;
});
// Toggle clicked
const clickedFilterName = filters[buttonIndex].innerHTML;
if (activeFilterNames.includes(clickedFilterName)) {
activeFilterNames = activeFilterNames.filter(f => f !== clickedFilterName);
} else {
activeFilterNames.push(clickedFilterName);
}
if (clickedFilterName === 'all') {
// Turn off the rest filters if `All` clicked
activeFilterNames = ['all'];
} else {
// Turn off `all` if haven any other
const activeFilterNamesWithoutItemNamedAll = activeFilterNames.filter(f => f !== 'all');
activeFilterNames = activeFilterNamesWithoutItemNamedAll.length ? activeFilterNamesWithoutItemNamedAll : ['all'];
}
// Add or remove `_active` class for each button
filters.forEach(function (f, i) {
if (activeFilterNames.includes(f.innerHTML)) {
document.querySelectorAll('[data-control]')[i].classList.add('_active');
} else {
document.querySelectorAll('[data-control]')[i].classList.remove('_active');
}
});
handleFilterTeaser(activeFilterNames);
}
function handleFilterTeaser(filters) {
const values = [];
document.querySelectorAll('.all').forEach(tag => {
// Add or remove `_hidden` class for each item
if (
[...tag.classList].find(function (clazz) {
return filters.includes(clazz);
})
) {
tag.classList.remove('_hidden');
// Collect visible items for display in `#choice` element
values.push(tag.innerHTML);
} else {
tag.classList.add('_hidden');
}
});
document.querySelector('#choice').innerHTML = values.join(', ');
}
https://codepen.io/Urnix/pen/OJwjPpR
Related
I'm actually occurring a situation when making an accordion. I already add a close button (x text) inside my div to close the accordion, but it won't close after I clicked on that. Btw, my reference design is from https://dribbble.com/shots/6584063-Daily-UI-Accordion-Cards-Experiment. It's only the example of the behavior. Like in my reference, I don't want to have an active class on the first time. Then when clicked the other tab, the current active class is inactive, and have an external close button.
document.addEventListener("DOMContentLoaded", (e) => {
const accordion = document.querySelectorAll('.a');
const button = document.querySelectorAll('b');
/* add Class from the div itself being clicked */
accordion.forEach((accs, idx) => {
accs.addEventListener('click', () => {
addActive(accs, idx);
});
});
function addActive(el, index) {
el.classList.add('active');
accordion.forEach((accs, idx) => {
if (idx !== index) {
accs.classList.remove("active");
}
});
}
/* remove class from button */
button.forEach(xer => {
xer.addEventListener('click', () => {
removeActive();
});
});
function removeActive() {
accordion.forEach(accs => {
accs.classList.remove('active');
})
}
})
.toggle {
display: none
}
.a .b {
display: none;
}
.a.active {
color: blue;
}
.a.active .b {
color: rgba(0, 0, 0, 1);
display: block;
cursor: pointer;
}
<div id="1" class="a"> Source
<div id="button-1" class="b"> x </div>
</div>
<div id="2" class="a"> Share
<div id="button-2" class="b"> x </div>
</div>
<div id="3" class="a"> Report
<div id="button-3" class="b"> x </div>
</div>
Please help me to fix that. Thank you so much.
There were two problems in your snippet:
const button = document.querySelectorAll('b'); will select all <b/> elements, not elements with class .b; updated below to const button = document.querySelectorAll('.b');
Your close button is inside your accordion. So, while your close function was working and removing the .active class, then the click handler for the accordion was triggering and immediately reopening it. Adding a e.stopPropagation(); inside the handler stopped the event from bubbling up to the parent and resolved the problem.
See below for a working example:
document.addEventListener("DOMContentLoaded", (e) => {
const accordion = document.querySelectorAll('.a');
const button = document.querySelectorAll('.b');
/* add Class from the div itself being clicked */
accordion.forEach((accs, idx) => {
accs.addEventListener('click', () => {
addActive(accs, idx);
});
});
function addActive(el, index) {
el.classList.add('active');
accordion.forEach((accs, idx) => {
if (idx !== index) {
accs.classList.remove("active");
}
});
}
/* remove class from button */
button.forEach(xer => {
xer.addEventListener('click', (e) => {
e.stopPropagation();
removeActive();
});
});
function removeActive() {
accordion.forEach(accs => {
accs.classList.remove('active');
})
}
})
.toggle {
display: none
}
.a .b {
display: none;
}
.a.active {
color: blue;
}
.a.active .b {
color: rgba(0, 0, 0, 1);
display: block;
cursor: pointer;
}
<div id="1" class="a"> Source
<div id="button-1" class="b"> x </div>
</div>
<div id="2" class="a"> Share
<div id="button-2" class="b"> x </div>
</div>
<div id="3" class="a"> Report
<div id="button-3" class="b"> x </div>
</div>
When clicking the arrows to change the displayed option, the incorrect options is shown.
The user should be able click on the option menu to toggle it open/cosed and be able to click on a option to select it. Alternatively, the arrows could be used to toggle through the options instead.
This is the problematic code:
<script>
$("#arrow_left_physics").click(function() {
var $selected = $(".left_menu_option_selected").removeClass("left_menu_option_selected");
var divs = $("#left_menu__variant_physics").children();
divs.eq((divs.index($selected) - 1) % divs.length).addClass("left_menu_option_selected");
$("#left_menu_open .button-text").text($($selected).text());
});
$("#arrow_right_physics").click(function() {
var $selected = $(".left_menu_option_selected").removeClass("left_menu_option_selected");
var divs = $selected.parent().children();
divs.eq((divs.index($selected) + 1) % divs.length).addClass("left_menu_option_selected");
$("#left_menu_open .button-text").text($($selected).text());
});
</script>
$("#menu_open").click(function() {
$("#menu").toggle();
});
$(".menu_option").click(function() {
if ($(this).hasClass(".menu_option_selected")) {} else {
$(".menu_option").removeClass("menu_option_selected");
$(this).addClass("menu_option_selected");
$("#menu_open .button_text").text($(this).text());
}
});
$("#arrow_left").click(function() {
var $selected = $(".menu_option_selected").removeClass("menu_option_selected");
var options = $("#menu").children();
options.eq((options.index($selected) - 1) % options.length).addClass("menu_option_selected");
$("#menu_open .button_text").text($($selected).text());
});
$("#arrow_right").click(function() {
var $selected = $(".menu_option_selected").removeClass("menu_option_selected");
var options = $("#menu").children();
options.eq((options.index($selected) + 1) % options.length).addClass("menu_option_selected");
$("#menu_open .button_text").text($($selected).text());
});
.menu_open {
Cursor: pointer;
}
.menu {
display: none;
position: absolute;
border: 1px solid;
}
.menu_option {
Cursor: pointer;
Padding: 5px;
}
.menu_option:hover {
Background-Color: black;
Color: white;
}
.menu_option_selected {
color: green;
Background-color: #00ff0a4d;
}
.menu_option_selected:hover {
color: green;
}
.arrow {
Cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<input class="arrow" type="button" id="arrow_left" value="❮" />
<input class="arrow" type="button" id="arrow_right" value="❯" />
</div>
<div>
<button class="menu_open" id="menu_open">
<span class="button_text">option1</span>
</button>
</div>
<div class="menu" id=menu>
<div class="menu_option menu_option_selected">option1</div>
<div class="menu_option">option2</div>
<div class="menu_option">option3</div>
<div class="menu_option">option4</div>
<div class="menu_option">option5</div>
<div class="menu_option">option6</div>
</div>
-It seems that the first click of the arrows isn't working and that the index function is incorrect somewhere.
The problem is this line:
$("#menu_open .button_text").text($($selected).text());
$($selected) is the option that was previously selected, so you're showing the text of the previous option, not the current option. (BTW, there's no need to wrap $selected in $(), since it's already a jQuery object.)
You should use $(".menu_option_selected").text() instead of $($selected).text() to get the current option.
You should also make the initial text of the button option1, so it matches the selected option.
$("#menu_open").click(function() {
$("#menu").toggle();
});
$(".menu_option").click(function() {
if ($(this).hasClass(".menu_option_selected")) {} else {
$(".menu_option").removeClass("menu_option_selected");
$(this).addClass("menu_option_selected");
$("#menu_open .button_text").text($(this).text());
}
});
$("#arrow_left").click(function() {
var $selected = $(".menu_option_selected").removeClass("menu_option_selected");
var options = $("#menu").children();
options.eq((options.index($selected) - 1) % options.length).addClass("menu_option_selected");
$("#menu_open .button_text").text($(".menu_option_selected").text());
});
$("#arrow_right").click(function() {
var $selected = $(".menu_option_selected").removeClass("menu_option_selected");
var options = $("#menu").children();
options.eq((options.index($selected) + 1) % options.length).addClass("menu_option_selected");
$("#menu_open .button_text").text($(".menu_option_selected").text());
});
.menu_open {
Cursor: pointer;
}
.menu {
display: none;
position: absolute;
border: 1px solid;
}
.menu_option {
Cursor: pointer;
Padding: 5px;
}
.menu_option:hover {
Background-Color: black;
Color: white;
}
.menu_option_selected {
color: green;
Background-color: #00ff0a4d;
}
.menu_option_selected:hover {
color: green;
}
.arrow {
Cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<input class="arrow" type="button" id="arrow_left" value="❮" />
<input class="arrow" type="button" id="arrow_right" value="❯" />
</div>
<div>
<button class="menu_open" id="menu_open">
<span class="button_text">option1</span>
</button>
</div>
<div class="menu" id=menu>
<div class="menu_option menu_option_selected">option1</div>
<div class="menu_option">option2</div>
<div class="menu_option">option3</div>
<div class="menu_option">option4</div>
<div class="menu_option">option5</div>
<div class="menu_option">option6</div>
</div>
Just another version, refactoring your javascript code with some Arrow functions.
const setButtonText = () => {
$("#menu_open .button_text").text(
$(".menu_option_selected").text()
);
}
const moveSelection = direction => {
var selected = $(".menu_option_selected")
var options = $("#menu").children()
var newIndex;
if (direction == 'right') {
newIndex = (options.index(selected) + 1) % options.length
} else {
newIndex = (options.index(selected) - 1) % options.length
}
selected.removeClass("menu_option_selected")
options.eq(newIndex).addClass("menu_option_selected")
setButtonText()
}
// inizilize menu button_text
setButtonText()
$("#arrow_left").click(() => moveSelection('left'));
$("#arrow_right").click( () => moveSelection('right'));
$("#menu_open").click( () => $("#menu").toggle());
$(".menu_option").click( function() {
$(".menu_option_selected").removeClass("menu_option_selected")
$(this).addClass("menu_option_selected")
setButtonText()
});
I'm trying to setup multiple-step form in which the first step is visible by default and rest of the steps are hidden with class "hide". I'd like to switch the class with Next and Back button so only one step is visible at a time. Could you please help with this (Already spent an hour on this)
<div class="steps">
<div class="step1">step1</div>
<div class="step2 hide">step2</div>
<div class="step3 hide">step3</div>
<div class="step4 hide">step4</div>
</div>
<div class="back">Back</div>
<div class="next">Next</div>
$('.next').click(function(){
$('div:not(.hide)').next().removeClass('hide');
$('.hide').prev().removeClass('hide')
})
Try combining the 2 actions into one, like so:
$('.next').click(function(){
$('.steps div:not(.hide)').addClass('hide').next().removeClass('hide');
})
That way, you add the .hide class on your current div and then remove it on the next one.
You can use something similar for the Back button, by replacing .next() with .previous()
$('.next').click(function() {
// find the div that is not hidden
var $current = $('.steps div:not(.hide)');
// only perform logic if there is a proceeding div
if ($current.next().length) {
// show the next div
$current.next().removeClass('hide');
// hide the old current div
$current.addClass('hide')
}
});
$('.back').click(function() {
// find the div that is not hidden
var $current = $('.steps div:not(.hide)');
// only perform logic if there is a preceeding div
if ($current.prev().length) {
// show the previous div
$current.prev().removeClass('hide');
// hide the old current div
$current.addClass('hide')
}
});
.hide { display: none; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="steps">
<div class="step1">step1</div>
<div class="step2 hide">step2</div>
<div class="step3 hide">step3</div>
<div class="step4 hide">step4</div>
</div>
<div class="back">Back</div>
<div class="next">Next</div>
You can add a current step variable to track the currently displayed step and two css for styling and showing your content.
jQuery(function($) {
let currentstep = 1;
let maxsteps = 4;
function showstep(step) {
let step_c = '.step' + step;
for (i = 1; i <= maxsteps; i++) {
var step_selector = '.step' + i;
$(step_selector).removeClass('show');
$(step_selector).addClass('hide');
}
$(step_c).removeClass('hide');
$(step_c).addClass('show');
};
$('.next').click(function() {
currentstep = currentstep + 1;
currentstep = (currentstep % (maxsteps + 1));
if (currentstep == 0) currentstep = 1;
showstep(currentstep);
});
$('.back').click(function() {
currentstep = currentstep - 1;
currentstep = (currentstep % (maxsteps + 1));
if (currentstep == 0) currentstep = 4;
showstep(currentstep);
});
});
.hide {
display: none;
}
.show {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="steps">
<div class="step1 show">step1</div>
<div class="step2 hide">step2</div>
<div class="step3 hide">step3</div>
<div class="step4 hide">step4</div>
</div>
<div class="back">Back</div>
<div class="next">Next</div>
I converted Taplar's answer to a jQuery plugin.
You are essentially navigating left or right by one, using the previous and next functions. These functions navigate through the sibling elements.
(function() {
$.fn.moveRight = function(className) {
var $curr = this.find('div:not(.' + className + ')');
if ($curr.next().length) $curr.next().removeClass(className);
else this.find('div:first-child').removeClass(className);
$curr.addClass(className);
return this;
};
$.fn.moveLeft = function(className) {
var $curr = this.find('div:not(.' + className + ')');
if ($curr.prev().length) $curr.prev().removeClass(className);
else this.find('div:last-child').removeClass(className);
$curr.addClass(className);
return this;
};
})(jQuery);
$('.next').on('click', (e) => $('.steps').moveRight('hide'));
$('.back').on('click', (e) => $('.steps').moveLeft('hide'));
.hide {
display: none;
}
.nav {
width: 260px;
text-align: center;
}
.nav .nav-btn::selection { background: transparent; }
.nav .nav-btn::-moz-selection { background: transparent; }
.nav .nav-btn {
display: inline-block;
cursor: pointer;
}
.steps {
width: 260px;
height: 165px;
border: thin solid black;
text-align: center;
line-height: 165px;
font-size: 3em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="steps">
<div class="step1">step1</div>
<div class="step2 hide">step2</div>
<div class="step3 hide">step3</div>
<div class="step4 hide">step4</div>
</div>
<div class="nav">
<div class="nav-btn back">[ << Back ]</div>
<div class="nav-btn next">[ Next >> ]</div>
</div>
I have a array of objects where i have a property of date_time, and in this array i wish to get the lenght of dates_times objects that are sooner with my current date_time.
Ex:
[data:[{date: "2017-02-24 16:41:51"}, {date: ""2017-02-21 16:41:51"}...],
last_clicked: "2017-02-24 19:41:51"]
I wish to get the length of objecs on "data" array that haves a date_time sooner than "last_clicked".
Maybe you can try something like this:
let length = data.filter(function(d) {
return new Date(d.date) < new Date(last_clicked)
}).length;
You can use Angulars-orderBy-filter. I set up an example for you, just swap out where I have the id key for your data's date key.
function exampleController($scope, exampleFactory, $filter) {
$scope.list = [];
$scope.reverse = false;
$scope.changeSortOrder = function() {
$scope.reverse = !$scope.reverse;
};
function getList() {
exampleFactory
.getList()
.then(function(list) {
// $scope.list = list;
//alternatively you could filter here and change the sort order with the button if needed at all.
$scope.list = $filter('orderBy')(list, '-id'); //where this would be replaced by date(note: the '-' in front of id changes the sort order ASC/DESC)
//$scope.list = $filter('orderBy')(list, 'id', true); //where 3rd argument is wether order should be reversed
});
}
getList();
}
function exampleFactory($http) {
var root = 'http://jsonplaceholder.typicode.com';
function getList() {
return $http.get(root + '/comments')
.then(function(resp) {
return resp.data;
});
}
return {
getList: getList
};
}
angular
.module('app', [])
.controller('exampleController', exampleController)
.factory('exampleFactory', exampleFactory);
.container-fluid {
background-color: #1D1F20;
color: #fff;
font-size: 14px;
font-weight: bold;
button {
margin-top: 20%;
}
}
ul li {
list-style: none;
margin: 10px;
}
.child-padding>div {
padding: 2px;
}
.col-md-2 {
position: fixed;
button {
margin-bottom: 10%;
}
}
.btn-circle {
width: 30px;
height: 30px;
text-align: center;
padding: 6px 0;
font-size: 12px;
line-height: 1.428571429;
border-radius: 50%;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<div class="container-fluid" ng-app="app">
<div class="container" ng-controller="exampleController">
<div class="row">
<div class="col-md-2 text-center">
<button class="btn btn-primary" type="button" ng-click="changeSortOrder()">Change Sort Order</button>
</div>
<div class="col-md-10 pull-right">
<ul class="ul">
<li ng-repeat="comment in list | orderBy: 'date': reverse track by $index">
<div class="child-padding">
<div>
<span ng-bind="comment.email"></span>
<span class="pull-right btn-info btn-circle" ng-bind="comment.id"></span>
</div>
<div ng-bind="comment.body"></div>
</div>
<div ng-bind="comment.name"></div>
</li>
</ul>
</div>
</div>
</div>
</div>
Use Date.parse to parse your date strings
DEMO JsFiddle
The following code will alert:
Sooner times: 1
var o = {
data:[
{date: "2017-02-24 16:41:51"},
{date: "2017-02-21 16:41:51"}
],
last_clicked: "2017-02-24 12:41:51"
};
function getSoonerLength(data, soonerThan) {
var count = 0;
for (var i = 0 ; i < data.length ; i++) {
var d = Date.parse(data[i].date);
var than = Date.parse(soonerThan);
if (d < than)
count++;
}
return count;
}
alert('Sooner times: ' + getSoonerLength(o.data, o.last_clicked))
Using the method I found here I'm trying to upgrade the functionality of my previous question.
Basically I want to cycle through my elements with previous and next buttons. I cannot use nextAll() and prevAll() functions because they are not all in the same wrapper, so I used the most upvoted solution from the first link. Next cycling works fine, and previous doesn't.
var $content = $(".big_container");
var $loaded_content = $(".details");
var $item_selector = $(".item");
var $previous = $('.previous');
var $next = $('.next');
if ($content.length > 0) {
$item_selector.on('click', function(e) {
e.preventDefault();
var $this = $(this);
load_gallery_container($this);
});
$next.on('click', function(e) {
e.preventDefault();
var $next_item = $content.find('.current').findNext('.item');
if ($content.find('.item').hasClass('current')) {
if ($next_item.length) {
$content.find('.item').removeClass('current');
}
load_gallery_container_next_prev($next_item);
}
});
$previous.on('click', function(e) {
e.preventDefault();
var $prev_item = $content.find('.current').findPrev('.item');
if ($content.find('.item').hasClass('current')) {
if ($prev_item.length) {
$content.find('.item').removeClass('current');
}
load_gallery_container_next_prev($prev_item);
}
});
}
//Next all items
$.fn.findNextAll = function(selector) {
var that = this[0],
selection = $(selector).get();
return this.pushStack(!that && selection || $.grep(selection, function(n) {
return that.compareDocumentPosition(n) & (1 << 2);
// if you are looking for previous elements it should be & (1<<1);
}));
}
$.fn.findNext = function(selector) {
return this.pushStack(this.findNextAll(selector).first());
}
//Previous all items
$.fn.findPreviousAll = function(selector) {
var that = this[0],
selection = $(selector).get();
return this.pushStack(!that && selection || $.grep(selection, function(n) {
return that.compareDocumentPosition(n) & (1 << 1);
// if you are looking for previous elements it should be & (1<<1);
}).reverse());
}
$.fn.findPrev = function(selector) {
return this.pushStack(this.findPreviousAll(selector).first());
}
function load_gallery_container_next_prev($container) {
$loaded_content.find('.div_content').html($container.data('content'));
$container.addClass('current');
}
function load_gallery_container($container) {
if ($container.hasClass("current")) {
$loaded_content.slideUp('slow', function() {
$(this).removeClass('open');
$container.removeClass("current");
});
} else {
var $insert_after = $container.parent('.wrappers'),
$current = $('.current');
$current.removeClass('current');
if ($current.parent('.wrappers').is($insert_after)) {
$loaded_content.find('.div_content').html($container.data('content'));
$container.addClass("current");
} else {
if ($loaded_content.hasClass("open")) {
$loaded_content.slideUp('slow', function() {
$(this).removeClass('open');
$container.removeClass("current");
$loaded_content.detach().insertAfter($insert_after);
$loaded_content.find('.div_content').html($container.data('content'));
});
} else {
$loaded_content.detach().insertAfter($insert_after);
$loaded_content.find('.div_content').html($container.data('content'));
}
$loaded_content.slideDown('slow', function() {
$container.addClass("current");
$(this).addClass('open');
});
}
}
setTimeout(function() {
$('html, body').animate({
scrollTop: $loaded_content.offset().top - 300
}, 500);
}, 600);
}
.big_container {
background: #141414;
display: block;
padding: 30px;
}
.wrappers {
width: 500px;
margin: 0 auto;
display: block;
}
.item {
width: 31%;
height: 100px;
margin-right: 1%;
margin-bottom: 30px;
text-align: center;
background: #ccc;
color: #fff;
display: inline-block;
}
.details {
background: #ddd;
width: 100%;
padding: 30px;
display: none;
}
.navigation {
display: block;
text-align: center;
color: #fff;
}
.previous,
.next {
font-size: 18px;
display: inline-block;
margin: 0 12px 10px;
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="big_container">
<div class="navigation">
<div class="previous">
<</div>
<div class="next">></div>
</div>
<div class="wrappers">
<div class="item" data-content="blabla bla bla blaaaa">Click on meee!</div>
<div class="item" data-content="blabla BLALALA bla blaaaa">Click on meee!</div>
<div class="item" data-content="blabla blBLALbla blaaaa">Click on meee!</div>
</div>
<div class="wrappers">
<div class="item" data-content="blabla bla bla randomness">Click on meee!</div>
<div class="item" data-content="blabla bla bla ">Click on meee!</div>
<div class="item" data-content="blabla bla bla weeee">Click on meee!</div>
</div>
<div class="wrappers">
<div class="item" data-content="blabla bla bla blaaaa">Click on meee!</div>
<div class="item" data-content="blabla bla bla???">Click on meee!</div>
<div class="item" data-content="I am done with blaaaaing">Click on meee!</div>
</div>
<div class="details">The content from div goes here:
<div class="div_content"></div>
</div>
</div>
Basically when I click previous I get sent back to first element in the list, and not to the previous one. So the findPrev() function needs modifying, and I tried a lot: changing .first() to .last() and .prev() and even .prevAll(); tried changing return that.compareDocumentPosition(n) & (1<<1);
Nothing changed that would make this go to previous item. Anybody has an idea what is wrong with it?
ANSWER
Ok, so the solution is this:
$.fn.findPreviousAll = function( selector ){
var that = this[ 0 ],
selection = $( selector ).get();
return this.pushStack(
!that && selection || $.grep( selection, function(n){
return that.compareDocumentPosition(n) & (1<<1);
// if you are looking for previous elements it should be & (1<<1);
}).reverse()
);
};
$.fn.findPrev = function( selector ){
return this.pushStack( this.findPreviousAll( selector ).first() );
};
What the findPreviousAll function was doing was returning an array from first element to the one I clicked on, so I just needed to reverse it! And a simple .reverse() did it :)
I updated the snippet so it works! :)
Change this:
var $prev_item = $content.find('.current').findPrev('.item');
if ($content.find('.item').hasClass('current')) {
if ($prev_item.length) {
$content.find('.item').removeClass('current');
}
load_gallery_container_next_prev($prev_item);
}
to this:
var $prev_item = $(".item")[$(".item").index($(".item.current"))-1];
if($prev_item){
$(".item.current").removeClass('current');
}
$($prev_item).addClass('current');
load_gallery_container_next_prev($prev_item);
and change this:
function load_gallery_container_next_prev($container) {
$loaded_content.find('.div_content').html($container.data('content'));
$container.addClass('current');
}
to this:
function load_gallery_container_next_prev($container) {
$loaded_content.find('.div_content').html($($container).attr("data-content"));
$container.addClass('current');
}
Here is the JSFiddle demo :)