Get index of element among siblings ignoring sibling with specific child - javascript

I have a specific use case I can't seem to find an answer to. Given the DOM elements below:
<div class="wrapper">
<div class="item"></div>
<div class="item">
<div class="foo"></div>
</div>
<div class="item">
<div class="bar"></div>
</div>
<div class="item"></div>
<div class="item selected"></div>
<div class="item"></div>
</div>
I need to find the index of the .selected element in regard to it's siblings. But I need to ignore any siblings that have the .foo child element (it will only ever be the direct child).
So typically to find the index of .item .selected you could use $(".item.selected").index() which gives 4, but since one item before it has a .foo child the correct answer is 3.
I thought, the best way to go about it was to grab all the siblings before the selected element (since siblings after it wouldn't shift it's index) and then count how many have a .foo child, and subtract that number from the selected index, so 4-1=3. I tried to do that like this:
var selectedIndex = $(".item.selected").index();
var fooCount = $(".item.selected").prevAll('.item > .foo').length;
var finalIndex = selectedIndex - fooCount;
The problem is, fooCount is coming up 0 instead of 3.

You can simply use .filter() and remove the preceding elements that have a given child.
const selected = $('.selected');
const foos = selected.prevAll().filter(function() {
return !($(this).find('.foo').length);
});
console.log(selected.index(), foos.length);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
<div class="item"></div>
<div class="item">
<div class="foo"></div>
</div>
<div class="item">
<div class="bar"></div>
</div>
<div class="item"></div>
<div class="item selected"></div>
<div class="item"></div>
</div>

Use this selector ".item:not(:has(.foo))" and then loop to find the specific index.
var index = $(".item:not(:has(.foo))")
.toArray()
.findIndex(function(item) {
return $(item).hasClass('selected');
});
console.log(index);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
<div class="item"></div>
<div class="item">
<div class="foo"></div>
</div>
<div class="item">
<div class="bar"></div>
</div>
<div class="item"></div>
<div class="item selected"></div>
<div class="item"></div>
</div>

You were close.
Change:
var fooCount = $(".item.selected").prevAll('.item > .foo').length;
… to:
var fooCount = $(".item.selected").prevAll('.item:has(.foo)').length;
Otherwise, you're looking for a sibling with class .foo, when you actually want a sibling that has a child with class .foo.
Snippet:
var selectedIndex = $(".item.selected").index();
var fooCount = $(".item.selected").prevAll('.item:has(.foo)').length;
var finalIndex = selectedIndex - fooCount;
console.log(finalIndex);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
<div class="item"></div>
<div class="item">
<div class="foo"></div>
</div>
<div class="item">
<div class="bar"></div>
</div>
<div class="item"></div>
<div class="item selected"></div>
<div class="item"></div>
</div>

You can combine:
:not(): selects all elements that do not match the given selector.
:has(): reduce the set of matched elements to those that have a descendant that matches the selector or DOM element.
:index(element): where element is the DOM element or first element within the jQuery object to look for.
Hence, you can change your code:
var selectedIndex = $(".item.selected").index();
to:
var selectedIndex = $('.item:not(:has(.foo))').index($('.item.selected'));
var selectedIndex = $('.item:not(:has(.foo))').index($('.item.selected'));
console.log(selectedIndex );
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
<div class="item"></div>
<div class="item">
<div class="foo"></div>
</div>
<div class="item">
<div class="bar"></div>
</div>
<div class="item"></div>
<div class="item selected"></div>
<div class="item"></div>
</div>

Related

Problem sorting divs using sort function inside jQuery each

I'm trying to sort inner elements inside parents.
In this simplified markup divs with class "down" supposed to go down (or up doesn't matter).
HTML
<div class="parent">
<div class="inner down">2</div>
<div class="inner down">2</div>
<div class="inner">1</div>
</div>
<div class="parent">
<div class="inner">1</div>
<div class="inner down">2</div>
<div class="inner">1</div>
</div>
My code that supposed to work but nothing happens. Divs are not moving.
$('.parent').each(function(){
var $elem = $(this).find('.inner').sort(function(a,b){
return a.className < b.className;
//return Math.random() >= 0.5; //doesn't make anything either
});
$(this).append($elem);
});
Desired output
<div class="parent">
<div class="inner">1</div>
<div class="inner down">2</div>
<div class="inner down">2</div>
</div>
<div class="parent">
<div class="inner">1</div>
<div class="inner">1</div>
<div class="inner down">2</div>
</div>
I don't realy understand what's wrong with my code (jQuery 3.3.1 is in use). Any suggestion?
Get all elements with down class and then re-append them to move them to down.
// use append method with callback to iterate over the
// element and append the eleemnt which is returned
$('.parent').append(function() {
// get all element with class down witin the current parent
// specify context as this(this refers to current parent element)
return $('.down', this)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="parent">
<div class="inner down">2</div>
<div class="inner down">2</div>
<div class="inner">1</div>
</div>
<div class="parent">
<div class="inner">1</div>
<div class="inner down">2</div>
<div class="inner">1</div>
</div>

Targetting the last element that has a tabindex attribute?

How do I target the last element of a given parent, that has a tabindex attribute?
For instance:
<div id="popup">
<div id="some-element" tabindex="0">
<div id="some-other-element" tabindex="0">
<div id="yet-another-element" tabindex="0">
</div>
In this case it would target #yet-another-element
You can use the attribute selector [...] combined with jQuery .last() :
let target = $('#popup [tabindex]').last();
console.log(target.attr('id'));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="popup">
<div id="some-element" tabindex="0">
<div id="some-other-element" tabindex="0">
<div id="yet-another-element" tabindex="0">
</div>
Another way is to use jQuery :last selector :
$('#popup [tabindex]:last');
let target = $('#popup [tabindex]:last');
console.log(target.attr('id'));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="popup">
<div id="some-element" tabindex="0">
<div id="some-other-element" tabindex="0">
<div id="yet-another-element" tabindex="0">
</div>
If you need to select a specific tabindex value :
$('#popup [tabindex="0"]:last');
Using JavaScript:
Use Document.querySelectorAll() with Attribute selectors to get all the elements first in the form of an array-like object. Then use Spread syntax (...) and array.length - 1 to take the element from the last index:
var div = document.querySelectorAll('#popup > div[tabindex="0"]');
var lastEl = [...div][div.length - 1];
console.log(lastEl.id)
<div id="popup">
<div id="some-element" tabindex="0"></div>
<div id="some-other-element" tabindex="0"></div>
<div id="yet-another-element" tabindex="0"></div>
</div>
Using jQuery: You can use :last as part of the selector:
var lastEl = $('#popup > div[tabindex="0"]:last');
console.log($(lastEl).prop('id'));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="popup">
<div id="some-element" tabindex="0"></div>
<div id="some-other-element" tabindex="0"></div>
<div id="yet-another-element" tabindex="0"></div>
</div>

Append before last child

I have a div with the ID wrapper, and I am using .append() to insert some content at the end of that div, like this:
$("#wrapper").append('<div class="content"><div class="subcontent">Some stuff</div></div>');
However, I also want the option to insert a new child before the last content div in the wrapper.
So if the HTML output looks like this:
<div id="wrapper">
<div class="content">
<div class="subcontent">
First
</div>
</div>
<div class="content">
<div class="subcontent">
Second
</div>
</div>
</div>
I want to insert an element before the last one, so I get this:
<div id="wrapper">
<div class="content">
<div class="subcontent">
First
</div>
</div>
<div class="content">
<div class="subcontent">
Third
</div>
</div>
<div class="content">
<div class="subcontent">
Second
</div>
</div>
</div>
How would I do this?
You could use .before() to add a sibling before the element:
$("#wrapper .content:last").before('<div class="content"><div class="subcontent">Third</div></div>');
.insertBefore() does the same thing with a different syntax, namely that you select the element to be added, and pass the element you want to add it before.
$("#wrapper .content:last").before('<div class="content"><div class="subcontent">Third</div></div>');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="wrapper">
<div class="content">
<div class="subcontent">
First
</div>
</div>
<div class="content">
<div class="subcontent">
Second
</div>
</div>
</div>
You can use insertBefore():
$('<div class="content"><div class="subcontent">Some stuff</div></div>').insertBefore('#wrapper > div:last');
Or before():
$('#wrapper > div:last').before('<div class="content"><div class="subcontent">Some stuff</div></div>');
This is how I got there:
http://jsfiddle.net/lharby/00tk6avg/
var htmlString = '<div class="subcontent">Some stuff</div>';
$(htmlString).insertBefore('.content div:last');
Select the last element with :last-of-type and use before() to append the new element:
$('.content:last-of-type').before('<div class="new">test</div>');
.new { color:red }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="wrapper">
<div class="content">
<div class="subcontent">
First
</div>
</div>
<div class="content">
<div class="subcontent">
Second
</div>
</div>
</div>
Use insertBefore:
$('<div class="content"><div class="subcontent">Third</div></div>').insertBefore('#wrapper .content:last');
Insert every element in the set of matched elements before the target.
Demo: http://api.jquery.com/insertBefore/
$( "<p>Test</p>" ).insertBefore( "#wrapper > div:last-child" );
You can last() for selecting last item, and before() for appending
$("#wrapper .content").last().before('<div class="content"><div class="subcontent">Some stuff</div></div>');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="wrapper">
<div class=" content ">
<div class="subcontent ">
First
</div>
</div>
<div class="content ">
<div class="subcontent ">
Second
</div>
</div>
</div>
you can use nth last child to select the second last div.
fiddle:
http://jsfiddle.net/08ta9wnL/
html:
<div id="wrapper">
<div class="content">
<div class="subcontent">
First
</div>
</div>
<div class="content">
<div class="subcontent">
Second
</div>
</div>
</div>
javascript:
$(document).ready(function(){
$("#wrapper div:nth-last-child(2)").append('<div class="content"><div class="subcontent">Some stuff</div></div>');
});
You can do like this
$("#wrapper .content:last").before("<div class="content"><div class="subcontent">Some stuff</div></div>");
Have a look at fiddle https://jsfiddle.net/48ebssso/

How to .Clone div without cloning the wrapper

How to clone the content of a div without copying the div itself with it?
I use this function, but it copies div-1 inside div-2.
$(function(){
var $div = $('.div-1').clone();
$('.div-2').html($div);
});
HTML:
<div class="div-1">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
<div class="div-2">
</div>
<div class="div-2">
</div>
GOAL:
<div class="div-1">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
<div class="div-2">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
<div class="div-2">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
You can clone the children elements using:
$(function(){
var $div = $(".div-1").children().clone(true,true);
$('.div-2').html($div);
});
Working Demo
You need to clone he children
$(function(){
var $div = $('.div-1').children().clone();
$('.div-2').html($div);
});
$('.div-2').html($('.div-1').html());
Try this it will work

Counting all Item class elements before and after header classes

Hey I am thinking of a Jquery solution to a problem. Below you can see I have items that follow after each heading now this is fine.
<div class="header"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="header"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="header"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
But there are times when there will be no items. Just headers:
<div class="header"></div>
<div class="header"></div>
<div class="header"></div>
<div class="item"></div>
In this case I would like to check if there are any items after each header. If there is none add paragraph text "Not Available" Like so:
<div class="header"></div>
<p>Not Available</p>
<div class="header"></div>
<p>Not Available</p>
<div class="header"></div>
<div class="item"></div>
Many Thanks for any help.
See if this works :
$('.header').filter(function(){
return $(this).next().hasClass('header');
// or return !$(this).next().hasClass('item');
}).after('<p>Not Available</p>')
This is my idea for you:
- You can add a class name groups to include the header class and the items class, like this:
<div class="groups">
<div class="header"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
<div class="groups">
....
</div>
....
Now you have a lot of groups. What you have to do is count every group and check if item exist or not:
$('.groups').each(function(){
if($(this).children('items').length > 0){
// found items class
}
else{
// not found any items class
$(this).append('<p>Not Available</p>');
}
});

Categories

Resources