How to cycle through siblings using jQuery? - javascript

I have the folowing code:
html:
<div class="container">
<div class="selected">A</div>
<div>B</div>
<div>C</div>
<div>D</div>
</div>
<button id="next">next!</button>
jQuery:
$("#next").click(function() {
$(".selected").removeClass("selected").next().addClass("selected");
});
What i want is loop through the divs in the container. I can do this to cycle:
$("#next").click(function() {
if ($(".selected").next().length == 0) {
$(".selected").removeClass("selected").siblings(":nth-child(1)").addClass("selected");
}
else {
$(".selected").removeClass("selected").next().addClass("selected");
}
});
But i think there is a simpler way. How can i make it simpler ? (I don't mind if you don't use the next() function).
jsFiddle: http://jsfiddle.net/S28uC/

I 'd prefer siblings.first() instead of siblings(":nth-child(1)"), but in essence you won't be able to wrap around without using some variant of next().length.
Update: If I were writing this from scratch, this is how I 'd do it:
$("#next").click(function() {
var $selected = $(".selected").removeClass("selected");
var divs = $selected.parent().children();
divs.eq((divs.index($selected) + 1) % divs.length).addClass("selected");
});
This approach is motivated by two factors:
When you want to cycle over a collection indefinitely, modulo comes to mind
Getting rid of the if makes for smarter-looking code
When setting the value of divs I preferred $selected.parent().children() over the equivalent $selected.siblings().add($selected) as a matter of taste -- there are practically endless possibilities.

One simple way is this :
$("#container").find("div:eq(0)").addClass("selected");

how about this.
...
var selected = $(".selected").removeClass("selected");
if (jQuery(selected).next().addClass("selected").length == 0
{jQuery(selected).siblings().first().addClass("selected");};
...
In old good AI manner you try to do the deed (addClass), if it worked (length <> 0) nothing more to do, otherwise you try again on the first of the siblings.

You can try this
var cont = $('.container'),
i = 0;
$("#next").on('click', function() {
cont.children().removeClass('selected');
i += 1;
if ( i === document.querySelectorAll('.container div').length ) { i = 0; }
cont.children().eq(i).addClass('selected');
});
var cont = $('.container'),
i = 0;
$("#next").on('click', function() {
cont.children().removeClass('selected');
// increase index for each click
i += 1;
// reset i if it reached to last index
//(hack to force next to go back to first element when we are at the end)
if ( i === document.querySelectorAll('.container div').length ) {
i = 0;
}
cont.children().eq(i).addClass('selected');
});
.selected {
background-color: yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="selected">A</div>
<div>B</div>
<div>C</div>
<div>D</div>
</div>
<button id="next">next!</button>
simply you will increase i for each click and when it reach the end (divs length ) it will be reset.

Related

How can I select every single element with JS or jQuery?

I want to select every single element in a document and make them color red when I scroll to them.
$(document).ready(function() {
$(document).on("scroll", animationDivs);
function animationDivs(event) {
var scrollPos = $(document).scrollTop();
var divs = $("*");
$(divs).each(function() {
var currLink = $(this);
if (currLink.position().top <= scrollPos && currLink.position().top + currLink.height() > scrollPos) {
currLink.style.color = "red";
}
});
};
});
I used this codes but didn't work.
using JS:
document.querySelectorAll('*')
.forEach(el => el.style.color = 'red')
Try it in the console of your browser to see how it works and here's a brief overview of DOM selection with JS vs jQuery.
This is a similar question with a variety of solutions.
First add some common css class on each divs and add following jquery.
$('.class-name').each(function() {
var currLink = $(this);
if (currLink.position().top <= scrollPos && currLink.position().top + currLink.height() > scrollPos) {
currLink.style.color = "red";
}
});
using jq, you could simple get all element withing the html by "*"
var items = $("*").css({background : "red"})
console.log(items)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div></div>
<p></p>
currLink is a jQuery object in your code. So use a jQuery method on it.
That would be the .css() method in your case.
And I suggest you use an else part to your condition so the elements does not turn red after the first single wheel spin... Since <body> is also collected in $("*").
$(document).ready(function() {
$(document).on("scroll", animationDivs);
function animationDivs(event) {
var scrollPos = $(document).scrollTop();
var divs = $("*");
$(divs).each(function() {
var currLink = $(this);
if (currLink.position().top <= scrollPos && currLink.position().top + currLink.height() > scrollPos) {
currLink.css({"color":"red"});
}else{
currLink.css({"color":"initial"});
}
});
};
});
.spacer{
height:500px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<br>
<span>Scroll me.</span>
<div class="spacer"></div>
<div>div
<p>paragraph</p>
<a>anchor</a>
<span>span</span>
</div>
<div class="spacer"></div>
By the way... Using an .each() loop on the $("*") collection on every scroll event is the worst jQuery usage I suppose I will ever see. I can assure you that you'll scratch your head pretty soon with a real web page with real content.
Every elements, including <br> and <script> and <link> etc. are collected using $("*") that way... And are compared in the loop. You should only use it when absolutely necessary and within at least a container to lower the amount of collected elements.... Like $(".some-class *").

JS hover negative space between items

So I have a container with a grid of items and I want to be able to detect hover between rows. Not the individual items.
Its important to remember that the number of items, in the container and per row, will change.
My container of items currently looks like this
<div class="container">
<div class="hover-placeholder"></div>
<div class="row">
<!-- Start : Items -->
<div class="col-md-3">...</div>
<div class="col-md-3">...</div>
<div class="col-md-3">...</div>
<div class="col-md-3">...</div>
...
<div class="col-md-3">...</div>
<!-- End : Items -->
</div>
</div>
Preferably I DO NOT want to put a placeholder element every 4th item. Mainly because on smaller screens the number of items per row will reduce. This is why in my example above I have a single placeholder outside the grid that I want to transform: translateY(..) to the position between the hovered rows.
This is what I have currently: https://jsfiddle.net/0t8c0h4m/
Its nowhere near the result I am after but I have got to the point where I am overthinking it and getting stuck.
Any help would be great!
UPDATE
The goal for this functionality is when the user hovers the negative space, the .hover-placeholder will translate to that position and become visible. And when clicked will add a permanent separator between the rows.
SUCCESS!
I have solved my issue! Thank you all for your help.
Here is my solution: https://jsfiddle.net/0t8c0h4m/9/
I think that you are complicating stuff too much if you are only looking for the hover effect between the elements and nothing else.
Updated:
var offsets;
function get_offsets() {
offsets = {};
$('.card').each(function() {
var $this = $(this);
var card_offset = $this.offset().top;
offsets[card_offset] = {
ele: $this
};
})
}
get_offsets();
function get_last_ele(mouse_location) {
var element;
var previous_key;
$.each(offsets, function(key, obj) {
if (key > mouse_location && previous_key > 0) {
element = offsets[previous_key].ele;
return false;
}
previous_key = key;
})
return element;
}
$('.container').mousemove(function(e) {
if ($(e.target).parents('.row').length == 0) {
var last_item_row = get_last_ele(e.pageY)
console.log(last_item_row.text())
}
});
JSFiddle: https://jsfiddle.net/0t8c0h4m/6/
I'm only providing the code that gets the last item on the row before the space you are hovering. From there you can append the line or transition the placeholder the way you like it.
Please try with following script
(function($) {
'use strict';
// Count items per row
function items_per_row($collection) {
var count = 0;
$collection.each(function() {
var $this = $(this);
if ($this.prev().length > 0) {
if ($this.position().top !== $this.prev().position().top) {
return false;
} else {
count++;
}
} else {
count++;
}
});
var last = count ? $collection.length % count : 0,
rows = count ? Math.round($collection.length / count) : 0;
if (last == 0) {
last = count;
}
return {
count: count,
last: last,
rows: rows
}
};
// On card hover, print current row
$('.card').mouseenter(function() {
var $card = $(this);
var item_count = items_per_row( $('.card') ),
index = $(this).index(),
current_row = Math.floor(index / item_count.count) + 1;
$('pre').text( $card.find('.inner').text() + ' is in row '+ current_row );
});
$('.card').mouseout(function(){
$('pre').text('');
});
})(jQuery);
So I have come up with a solution to show dividers between each row using CSS. I just have been overthinking this issue.
I have added a dividing element after each item and with css nth-child() I can show specific dividers at each break point.
I have also added the grouping functionality I was aiming for.
Updated example: https://jsfiddle.net/0t8c0h4m/9/

How to show a specific amount of child divs?

I am not sure if this is possible...
If you have f.ex.
<div id="parent">
<div id="child1"></div>
<div id="child2"></div>
<div id="child3"></div>
<div id="child4"></div>
<div id="child5"></div>
<div id="child6"></div>
</div>
How could you, with jquery or javascript (or anything for that matter), just show the first two?
You can use :gt() jQuery selector.
$("#parent>div:gt(1)").hide()
Actually, if you want to show incrementally, it is better to hide everything first and then use :lt() jQuery selector to show.
$("#parent>div").hide();
var n = 2;
$("#parent>div:lt(" + n + ")").show();
el.click(function () {
n += 5;
$("#parent>div:lt(" + n + ")").show();
});
You can do this with CSS:
#parent div:nth-child(n+3) {
display: none;
}
https://developer.mozilla.org/en-US/docs/Web/CSS/%3Anth-child
JsFiddle
Here's a way you could do it in jQuery:
// hide all the children
$("#parent>div").hide();
// unhide the two we care about
$("#child1").show();
$("#child2").show();
If you don't have known IDs for your elements, here's a more general solution:
$("#parent>div~div~div").hide();
You could write jQuery code like so:
var visibleIndexes = [0, 1]
$("#parent").children().each(function(index) {
if(visibleIndexes.indexOf(index) === -1){
$(this).hide();
} else {
$(this).show();
}
});
You can store indexes which you want to show in variable visibleIndexes or any other variable and pass it to this function
A simple iterative approach:
$( document ).ready(function() {
var i = 0;
var somevalue = 3;
$("#parent").children("div").each(
function () {
if(i > somevalue) $(this).hide();
i++;
});
});

How to reduce 180 lines of code down to 20 in Javascript?

I have a lot of click handler functions which are almost (textually and functionally) identical. I've got a menu with maybe 10 items in it; when I click on an item, the click handler simply makes one div visible, and the other 9 div's hidden. Maintaining this is difficult, and I just know there's got to be a smart and/or incomprehensible way to reduce code bloat here. Any ideas how? jQuery is Ok. The code at the moment is:
// repeat this function 10 times, once for each menu item
$(function() {
$('#menuItem0').click(function(e) {
// set 9 divs hidden, 1 visble
setItem1DivVisible(false);
// ...repeat for 2 through 9, and then
setItem0DivVisible(true);
});
});
// repeat this function 10 times, once for each div
function setItem0DivVisible(on) {
var ele = document.getElementById("Item0Div");
ele.style.display = on? "block" : "none";
}
Create 10 div with a class for marking
<div id="id1" class="Testing">....</div>
<div id="id2" class="Testing">....</div>
<div id="id3" class="Testing">....</div>
and apply the code
$('.Testing').each(function() {
$(this).click(function() {
$('.Testing').css('display', 'none');
$(this).css('display', 'block');
}
}
$(document).ready(function (){
$("div").click(function(){
// I am using background-color here, because if I use display:none; I won't
// be able to show the effect; they will all disappear
$(this).css("background-color","red");
$(this).siblings().css("background-color", "none");
});
});
Use .siblings() and it makes everything easy. Use it for your menu items with appropriate IDs. This works without any for loops or extra classes/markup in your code. And will work even if you add more divs.
Demo
Fiddle - http://jsfiddle.net/9XSJW/1/
It's hard to know without an example of the html. Assuming that there is no way to traverse from the menuItem to ItemDiv - you could use .index and .eq to match up the elements based on the order they match with the selector.
var $menuItems = $("#menuItem0, #menuItem1, #menuItem2, ...");
var $divs = $("#Item0Div, #Item1Div, #Item2Div, ...");
$menuItems.click(function(){
var idx = $(this).index();
// hide all the divs
$divs.hide()
// show the one matching the index
.eq(idx).show();
})
Try
function addClick(i) {
$('#menuItem'+i).click(function(e) {
// set nine divs hidden, 1 visble
for( var j = 0; j < 10; ++j ) {
var ele = document.getElementById("Item"+j+"Div");
ele.style.display = (i == j ? "block" : "none");
}
});
}
// One click function for all menuItem/n/ elements
$('[id^="menuItem"]').on('click', function() {
var id = this.id; // Get the ID of the clicked element
$('[id^="Item"][id$="Div"]').hide(); // Hide all Item/n/Div elements
$('#Item' + id + 'Div').show(); // Show Item/n/Div related to clicked element
});
Obviously this would be much more logical if you were using classes instead:
<elem class="menuItem" data-rel="ItemDiv-1">...</elem>
...
<elem class="ItemDiv" id="ItemDiv-1">...</elem>
$('.menuItem').on('click', function() {
var rel = $(this).data('rel'); // Get related ItemDiv ID
$('.ItemDiv').hide(); // Hide all ItemDiv elements
$('#' + rel).show(); // Show ItemDiv related to clicked element
});
Save the relevant Id's in an array - ["Item0Div", "Item1Div", ...]
Create a generic setItemDivVisible method:
function setItemDivVisible(visible, id) {
var ele = document.getElementById(id);
ele.style.display = visible ? "block" : "none";
}
And set your click handler method to be:
function(e) {
var arrayLength = myStringArray.length;
for (var i = 0; i < idsArray.length; i++) {
setItemDivVisible(idsArray[i] === this.id, idsArray[i]);
}
}
I think this will do the trick

javascript only - how to filter a specific div class?

I have many DIVs, let's say 100, split by categories like that :
<div id="div1" class="cat01">text</div>
<div id="div2" class="cat02">text</div>
<div id="div3" class="cat01">text</div>
<div id="div4" class="cat02">text</div>
<div id="div5" class="cat03">text</div>
<div id="div6" class="cat01">text</div>
And I want to filter a specific class
Let's say I click on a button "filter only cat02"
I then have only this on the page :
<div id="div2" class="cat02">text</div>
<div id="div4" class="cat02">text</div>
I do not have to use a class to define the categories, but it seems the appropriate solution...
Thanks you VERY much for your help!
EDIT : much clearer
Here is the file :
<div id="principal">
<div class="abc1 categ1">Text0</div>
<div class="abc5 categ3">Text0</div>
<div class="abc4 categ2">Text1</div>
<div class="abc7 categ1">Text0</div>
<div class="abc2 categ2">Text2</div>
<div class="abc4 categ3">Text0</div>
<div class="abc6 categ1">Text0</div>
<div class="abc7 categ2">Text3</div>
</div>
and I want this :
Text1
Text2
Text3
You can do something like this:
var list = document.getElementsByClassName('cat02');
for (var i = 0; i < list.length; i++) {
// this will remove the node from the page
list[i].parentNode.removeChild(list[i]);
// if you just want to hide it, you can do this:
// list[i].style.display = 'none';
}
Note that getElementsByClassName isn't supported by most browsers -- for those that don't, you may have to use a custom implementation such as the one here.
Update: If all your DIVs are direct children of a single DIV and they each contain only one class, it makes the task much simpler. You can skip getElementsByClassName and instead just iterate over the children:
function showOnly(parent, className) {
className = ' ' + className + ' ';
var e = parent.firstChild;
while (e != null) {
if (e.nodeType == 1) {
if ((' ' + e.className + ' ').indexOf(className) > -1)
e.style.display = 'block';
else
e.style.display = 'none';
}
e = e.nextSibling;
}
}
showOnly(document.getElementById('masterdiv'), 'cat02');
Edit: There were a couple of errors previously that I've fixed now. The indexOf comparison should be > -1 instead of > 0 and also the list of children includes empty text nodes (spaces between tags) that should be ignored, hence the check for e.nodeType == 1.
Seem like jQuery would help a lot here. You could just call $("div[class!='class02']") and get an array of items you want. Then, you could call .addClass('hidden') or whatever you need to do to the other items.
A brute force solution:
var list = document.getElementsByTagName('div');
for (var i = 0; i < list.length; i++) {
if (list[i].className != 'cat02') {
list{i].style.display = 'hidden';
}
}
Wrap this in a function to something like this:
function filterDivs(nameToFilter) {
var list = document.getElementsByTagName('div');
for (var i = 0; i < list.length; i++) {
if (!contains(list[i].className.split(' '), nameToFilter)) {
list{i].style.display = 'hidden';
}
}
}
EDIT:
using this function to search for strings in an array that I found here
function contains(a, obj) {
var i = a.length;
while (i--) {
if (a[i] === obj) {
return true;
}
}
return false;
}
With only 100 divs, it should run pretty quickly, but if you increase this amount a lot, it will become slow.
I also recommend that you don't remove items from a collection while iterating through it, that will cause you trouble. Hide them instead, or work with different collections.

Categories

Resources