jQuery Check if Element is in results of .prevAll() or .nextAll() - javascript

I'm essentially creating a wizard with tabs on a web app and tabs before the active one will be styled one way (in green) indicating completion, and tabs after it styled a different way to indicate not complete (in gray).
<div class="tabContainer">
<ul class='tabs'>
<li>1</span></li>
<i class="icon-arrow-right"></i>
<li>2</span></li>
<i class="icon-arrow-right"></i>
<li>3</span></li>
<i class="icon-arrow-right"></i>
<li>4</span></li>
<i class="icon-arrow-right"></i>
<li>5</span></li>
</ul>
</div>
In a custom function I'm writing called setTab(), I pass which tab I want to be active and then attempt to modify all other tabs to have styles indicated above.
function setTab(tabId) {
var $active, $content, $links = $('ul.tabs').find('a');
// Make tab active
$active = $($links.filter('[href="#tab'+tabId+'"]')[0] || $links[0]);
$active.addClass('active');
$active.find('span').addClass('badge btn-info');
$content = $($active.attr('href'));
// If Tab before, show as complete
var previousTabs = $active.prevAll('a');
previousTabs.removeClass('active');
previousTabs.find('span').removeClass('btn-info').addClass('badge btn-success');
// If Tab after, show as incomplete
var nextTabs = $active.nextAll('a');
nextTabs.removeClass('active');
nextTabs.find('span').removeClass('btn-info').removeClass('btn-success');
// Hide the remaining content
$links.not($active).each(function () {
$($(this).attr('href')).hide();
});
// Show Content
$content.show();
}
I'm looking for a way to check if an element is in the results of a prevAll() or nextAll() command in jQuery.
I've tried to figure it out using $.inArray and $.each but haven't had much luck. I'm guessing the first didn't work because the result set is an object, not an array. And the second one doesn't work because I need to compare it to an element which isn't available inside the $.each function. I'm sure with enough tweaking I could figure that one out, but there has to be a better way.
What is the best way of determining whether an element exists before or after another element, and if it is using the prevAll and nextAll methods I've been attempting, how can I do a boolean match with that result set?

You could use index:
$('element').nextAll().index(el) != -1

If you're passed the selected badge, you can avoid all of this by just marking all the previous badges as "completed". Something like:
var setTab = function (tabBadge) {
tabBadge.prevAll('.badge').addClass('completed').removeClass('inProgress todo');
tabBadge.addClass('inProgress').removeClass('completed todo');
// ignore the addClass here if the gray badges are the default style
tabBadge.nextAll('.badge').addClass('todo').removeClass('inProgress completed');
};
Note this functionality is very similar to star rating plugins, which is where the prevAll idea comes from.

compare the native JS elements inside a loop :
var set = $('element').nextAll(),
elem = $('#someElement'),
in_collection = false;
set.each(function() {
if ( this === elem.get(0) ) in_collection = true;
});
or you can use is() to check if any elements inside a collection matches a selector:
var set = $('element').nextAll(),
elem = $('#someElement'),
in_collection = set.is(elem);

Using a combination of various answers, I made something work:
/**
* Set active tab and adjust other tabs in process
*/
function setTab(tabId) {
var $active, $content, $links = $('ul.tabs').find('a');
// Make tab active
$active = $($links.filter('[href="#tab'+tabId+'"]')[0] || $links[0]);
$active.addClass('active');
$active.find('span').addClass('badge btn-info');
$content = $($active.attr('href'));
// If Tab before, show as complete
var previousTabs = $active.parent().prevAll('li').find('a');
previousTabs.each(function(index, el) {
$(el).removeClass('active');
$(el).find('span').removeClass('btn-info').addClass('badge btn-success');
});
// If Tab after, show as incomplete
var nextTabs = $active.parent().nextAll('li').find('a');
nextTabs.each(function(index, el) {
$(el).removeClass('active');
$(el).find('span').removeClass('btn-info').removeClass('btn-success');
});
// Hide the remaining content
$links.not($active).each(function () {
$($(this).attr('href')).hide();
});
// Show Content
$content.show();
}
setTab(3);
Looks like the DOM is what was messing me up...I needed to go up a level. Thanks for all the great answers!

Related

Make div appear or disappear with javascript

I am attempting to make a few divs behave the way I want them to. Here is my relevant HTML:
<ul class="services">
<li class="business-formation" id="services-li-1">Business Formation</li>
<li class="domestic-relations" id="services-li-2">Domestic Relations</li>
<li class="estate-probate" id="services-li-3">Estate & Probate</li>
</ul>
<div class="business-formation-list" id="business-formation-list">
<ul>
<li>Items go here</li>
</ul>
</div>
<div class="domestic-relations-list" id="domestic-relations-list">
<ul>
<li>Items go here</li>
</ul>
</div>
<div class="estate-probate-list" id="estate-probate-list">
<ul>
<li>Items go here</i>
</ul>
</div>
I want the divs to appear and disappear when the corresponding li is clicked (they are links). Here is my Javascript:
document.getElementById('services-li-1').style.cursor = "pointer";
document.getElementById('services-li-2').style.cursor = "pointer";
document.getElementById('services-li-3').style.cursor = "pointer";
const div1 = document.querySelector('business-formation-list');
const div2 = document.querySelector('domestic-relations-list');
const div3 = document.querySelector('estate-probate-list');
const click2 = document.getElementById('services-li-2');
document.addEventListener('click', (event) => {
if (event.click2.className = 'domestic-relations') {
div1.style.display = 'none';
div2.style.display = 'block';
div3.style.display = 'none';
}
});
This doesn't make anything happen, but what I wanted it to do is to make the second div appear when the li with the class name "domestic-relations" is clicked. What am I doing wrong? Thanks!
If you're using querySelector, you need to tell it that it's a class. You do that by adding . before the class in question.
So document.querySelector('business-formation-list') should be document.querySelector('.business-formation-list').
However, if you're only using it once, it should be an ID, not a class.
Im also fairly new to this and had a similar problem to yours. I came up with a solution, so it might help you as well. The solution is quite long so I hope you can bear with me.
first thing is to change your your div class "domestic/estate/business" to just "list". When you're doing the css for it, you'll want to apply the same style to them. If you need more styling, you can always target the IDs or add another css class.
Once you've done that, apply to that list "display: none;". This will hide all your ul's at once.
The javascript looks something like this. i've added some description as to why I chose to do what I did.
var serviceType = document.querySelectorAll(".serviceType");
var serviceTypeArray = Array.prototype.slice.call(serviceType); // this will
//change your serviceType from a nodelist to an Array.
var serviceDescription = document.querySelectorAll(".list"); // to grab the
//list of descriptions and make it into an "array"
for ( i = 0 ; i < serviceTypeArray.length ; i++) {
var services = serviceTypeArray[i]; // I created this variable to store the
// variable "i"
services.addEventListener( "click" , displayServices); }
// created this "for loop" to loop through every element inside the array
//and give it the "click" function
function displayServices() {
var servicePosition = serviceTypeArray.indexOf(this); // when we click the
// event, "this" will grab the position of the clicked item in the array
if (serviceTypeArray[servicePosition].hasAttribute("id") == false) { //
// the hasAttribute returns values of true/false, since initially the
// serviceTypeArray didn't have the "id" , it returns as false
serviceTypeArray[servicePosition].setAttribute( "id" , "hide"); // I've
// added the "setAttribute" function as a unique indicator to allow
// javascript to easily hide and show based on what we are clicking
serviceDescription[servicePosition].style.display = "block"; // When you
//click one of the serviceType, it returns an the index number (aka the
// position it is inside the arrayList)
//this gets stored inside servicePosition(aka the actual number gets
// stored).
// Since i've made an array to store the the description of each service,
// their position in the array correspond with the service's position
//inside the serviceType array made earlier; therefore, when I press one
// particular serviceType (ie i clicked on "business formation"), the
// appropriate text will pop up because the position of both the name and
// its description are in the same position in both arrays made
} else if (serviceTypeArray[servicePosition].hasAttribute("id") == true ) {
//since the serviceType now has an attribute, when we check it, it'll come
// back as true
serviceTypeArray[servicePosition].removeAttribute("id"); //we want to
// remove the id so next time we click the name again, the first "if"
// condition is checked
serviceDescription[servicePosition].style.display = "none"; //this is
// so that the display goes back to "none"
}
}
This will allow to click any of the names and display/hide them at will. you can even show 2/3 or all three of them, it's up to you. I hope this helps and I hope the explanation is clear enough!
Let me know if you have any questions for this!

jQuery slideDown not working on element with dynamically assigned id

EDIT: I cleaned up the code a bit and narrowed down the problem.
So I'm working on a Wordpress site, and I'm trying to incorporate drop-downs into my menu on mobile, which means I have to use jQuery to assign classes and id's to my already existing elements. I have this code that already works on premade HTML, but fails on dynamically created id's.
Here is the code:
...
var menuCount = 0;
var contentCount = 0;
//find the mobile menu items
var submenus = $('[title="submenu"]');
if (submenus.length && submenus.parent('.fusion-mobile-nav-item')) {
console.log(submenus);
submenus.addClass('dropdown-title').append('<i id="dropdown-angle" class="fa fa-angle-down" aria-hidden="true"></i>');
submenus.each(function() {
$(this).attr("href", "#m" + menuCount++);
})
var content = submenus.parent().find('ul.sub-menu');
content.addClass('dropdown-content');
content.each(function() {
$(this).attr("id", "m" + contentCount++);
})
}
$(document).on('click', '.dropdown-title', function(e) {
var currentAttrValue = $(this).attr('href');
if ($(e.target).is('.d-active') || $(e.target).parent('.dropdown-title').is('.d-active')) {
$(this).removeClass('d-active');
$(currentAttrValue).slideUp(300).removeClass('d-open');
} else {
$('.dropdown-title').removeClass('d-active');
$('.dropdown-content').slideUp(300).removeClass('d-open');
$(this).addClass('d-active');
console.log($(currentAttrValue));
//THIS LINE FAILS
$(currentAttrValue).slideDown(300).addClass('d-open');
}
e.preventDefault();
});
I've registered the elements with the class dropdown-title using $(document).on(...) but I can't figure out what I need to do to register the elements with the custom ID's. I've tried putting the event callback inside the .each functions, I've tried making custom events to trigger, but none of them will get the 2nd to last line of code to trigger. There's no errors in the console, and when I console log the selector I get this:
[ul#m0.sub-menu.dropdown-content, context: document, selector: "#m0"]
0
:
ul#m0.sub-menu.dropdown-content
context
:
document
length
:
1
selector
:
"#m0"
proto
:
Object[0]
So jQuery knows the element is there, I just can't figure out how to register it...or maybe it's something I'm not thinking of, I don't know.
If you are creating your elements dynamically, you should be assigning the .on 'click' after creating those elements. Just declare the 'on click' callback code you posted after adding the ids and classes instead of when the page loads, so it gets attached to the elements with .dropdown-title class.
Check this jsFiddle: https://jsfiddle.net/6zayouxc/
EDIT: Your edited JS code works... There also might be some problem with your HTML or CSS, are you hiding your submenus? Make sure you are not making them transparent.
You're trying to call a function for a attribute, instead of the element. You probably want $(this).slideDown(300).addClass('d-active'); (also then you don't need $(this).addClass('d-active'); before)
Inside submenus.each loop add your callback listener.
As you are adding the class dropdown-title dynamically, it was not available at dom loading time, that is why event listener was not attached with those elemnts.
var menuCount = 0;
var contentCount = 0;
//find the mobile menu items
var submenus = $('[title="submenu"]');
if (submenus.length && submenus.parent('.fusion-mobile-nav-item')) {
console.log(submenus);
submenus.addClass('dropdown-title').append('<i id="dropdown-angle" class="fa fa-angle-down" aria-hidden="true"></i>');
submenus.each(function() {
$(this).attr("href", "#m" + menuCount++);
// add callback here
$(this).click( function(e) {
var currentAttrValue = $(this).attr('href');
if ($(e.target).is('.d-active') || $(e.target).parent('.dropdown-title').is('.d-active')) {
$(this).removeClass('d-active');
$(currentAttrValue).slideUp(300).removeClass('d-open');
} else {
$('.dropdown-title').removeClass('d-active');
$('.dropdown-content').slideUp(300).removeClass('d-open');
$(this).addClass('d-active');
console.log($(currentAttrValue));
$(currentAttrValue).slideDown(300).addClass('d-active');
}
e.preventDefault();
});
})
var content = submenus.parent().find('ul.sub-menu');
content.addClass('dropdown-content');
content.each(function() {
$(this).attr("id", "m" + contentCount++);
})
}
Turns out my problem is that jQuery is adding to both the mobile menu and the desktop menu, where the desktop menu is being loaded first when I search for that ID that's the one that jQuery finds. So it turns out I was completely wrong about my suspicions.

How to keep the selected menu active using jquery, when the user comes back to page?

I have a typical menu structure -
<Ul class="nav">
<li>Menu1</li>
<li>menu2</li>
-------
</ul>
When I click on certain menu, as per my jquery written on load of layout.html, it selects particular menu.
<script>
jQuery(function(){
jQuery('.nav>li>a').each(function(){
if(this.href.trim() == window.location)
$(this).addClass("selected");
});
</script>
But on that page if I click on certain link which takes me on some other page and then when I come back the menu item does not remain selected.
How can I modify my jquery to achieve this?
Thanks in advance !
As SJ-B is saying, HTML5 Web Storage is a good solution.
If you don't intend to click more than one or two pages away from the page with your list menu, you could add a query to the link that takes you away form the page e.g. the id of one of your list menus.
href="somepage.html could become something like this href="somepage.html?menu_id=menu5
When using window.history.back(), you could then fish the id out of the URL using window.location.search and use id to select the list menu.
You can use simple css code. Use active attribute like
a:active
{
//Some style
}
You can use below code to achieve this.
var lastele=siteurl.substring(siteurl.lastIndexOf("/")+1);
jQuery(".nav>li> a").each(function(){
var anchorhref=jQuery(this).attr("href");
var finalhref=anchorhref.substring(anchorhref.lastIndexOf("/")+1);
if(finalhref==lastele){
jQuery(this).addClass("selected");
}
});
I would do something like this :
<ul class="nav">
<li id="home">Home</li>
<li id="contact">Contact</li>
</ul>
Javascript :
// http://mywebsite.com#home
// location.hash === '#home'
jQuery('.nav ' + location.hash).addClass('selected');
Try to use Session Object of HTML5.
sessionStorage.varName = id of selected item.
on load just check if the sessionStorage.varName has value or undefined, if not then get the value
`var value = sessionStorage.varName;` and set it.
Well there could be many ways, on which is this which i like and always use:
It works when you path name is same as your link name For e.g. yourwebsite.com/Menu1
function setNavigation() {
var n = window.location.pathname,t;
n = n.replace("/", "");
t = $("ul li:contains(" + n + ")");
t.addClass("active");
}
You can than define styling in your active class as you like.
I stumbled upon this when googling for something similar. I have a JQueryUI accordion menu. My menu is in an included script (classic asp), so it is on every page but I think it is a similar situation. I cobbled something together based on SJ-B's answer (don't know why it was down voted).
I have this:
function saveSession(id) {
if (window.sessionStorage) {
sessionStorage.activeMenu = $("#jqmenu").accordion("option", "active") ;
sessionStorage.activeLink = id ;
}
}
and this
$(function() {
//give every li in the menu a unique id
$('#jqmenu a').attr('id', function(i) {
return 'link'+(i+1);
});
var activeMenu = 0;
var activeLink = "";
if (window.sessionStorage) {
activeMenu = parseInt(sessionStorage.activeMenu);
activeLink = sessionStorage.activeLink;
}
$("#" + activeLink).parent().addClass("selectedmenu");
$("#jqmenu").accordion({collapsible: true, active: activeMenu, heightStyle: "content", header: "h3"});
$("#jqmenu a").click(function() { saveSession($(this).attr('id')); });
});
OK, a bit untidy and cobbled together from various suggestions (I'm still learning), but it seems to work. Tried on IE11 and Firefox. Chrome can't find localhost but that's another story.
add lines below
<script>
$(function(){
$("a[href='"+window.location+"']").addClass("selected");
});
</script>
var url = window.location.pathname,
urlRegExp = new RegExp(url.replace(/\/$/, '') + "$");
$('.nav li').each(function () {
if (urlRegExp.test(this.href.replace(/\/$/, ''))) {
$(this).addClass('active');
}
});

jQuery Accordion | Open first element on pageload & active state confusion

I am using the Javascript below to animate an accordion (it's a slightly modified variant of the one explained here: http://tympanus.net/codrops/2010/04/26/elegant-accordion-with-jquery-and-css3/.
Now I wanted to have the first element to be open on pageload, so I figured I just give it some sort of extra-class via Javascript (and define that .active state via CSS) to have it open up.
This worked, however if I hover over any but the first-element with said .active class, the first element keeps its state, and stays open until I hover over it at least once.
So, what I want is: the first element of my accordion is open and collapses if the user hovers over any of the elements that are not the first. I think I need to add a line in the hover function to either take the class away of the first element or to give the new element the active state, but I don't know how to do it and keep breaking the thing.
<script type="text/javascript">
jQuery(function() {
activeItem = jQuery("#accordion li:first");
jQuery(activeItem).addClass('active');
jQuery('#accordion > li, #accordion > li.heading').hover(
function () {
var jQuerythis = jQuery(this);
jQuerythis.stop().animate({'height':'280px'},500);
jQuery('.heading',jQuerythis).stop(true,true).fadeOut();
jQuery('.bgDescription',jQuerythis).stop(true,true).slideDown(500);
jQuery('.description',jQuerythis).stop(true,true).fadeIn();
},
function () {
var jQuerythis = jQuery(this);
jQuerythis.stop().animate({'height':'40px'},1000);
jQuery('.heading',jQuerythis).stop(true,true).fadeIn();
jQuery('.description',jQuerythis).stop(true,true).fadeOut(500);
jQuery('.bgDescription',jQuerythis).stop(true,true).slideUp(700);
}
);
});
</script>
Looks like this is happening because each accordion item has its own hover event that takes care of its own animation. You can refactor the code slightly to make this easier to understand and reuse:
var activeItem = jQuery("#accordion li:first");
jQuery('#accordion > li, #accordion > li.heading').hover(
function () { hoverMe(jQuery(this)); },
function () { unhoverMe(jQuery(this)); }
);
//This gets called when cursor hovers over any accordion item
var hoverMe = function(jQuerythis) {
//If the first item is still active
if (activeItem) {
contract(activeItem); //...Shrink it!
activeItem = false;
}
//Expand the accordion item
expand(jQuerythis);
};
//This gets called when cursor moves out of accordion item
var unhoverMe = function(jQuerythis) {
contract(jQuerythis);
};
//I have moved the hover animation out into a separate function, so we can call it on page load
var expand = function(jQuerythis) {
jQuerythis.stop().animate({'height':'280px'},500);
jQuery('.heading',jQuerythis).stop(true,true).fadeOut();
jQuery('.bgDescription',jQuerythis).stop(true,true).slideDown(500);
jQuery('.description',jQuerythis).stop(true,true).fadeIn();
};
//I have moved the unhover animation out into a separate function, so we can contract the first active item from hoverMe()
var contract = function() {
jQuerythis.stop().animate({'height':'40px'},1000);
jQuery('.heading',jQuerythis).stop(true,true).fadeIn();
jQuery('.description',jQuerythis).stop(true,true).fadeOut(500);
jQuery('.bgDescription',jQuerythis).stop(true,true).slideUp(700);
};
//Now expand the first item
expand(activeItem);
I have put together a simplified version demonstrating the logic. Please let me know how you get on.

remove class from current group

The easiest way to see the problem is checking the code here: http://www.studioimbrue.com/beta
What I need to do is once a thumbnail is clicked, to removed the "selected" class from all other thumbnails that are in this same or without removing them from the other galleries on the page. Right now, I have everything working except the class removal. Someone helped me in another question but wasn't quite specific enough (my javascript skills aren't all that great!) I'm using jQuery. Thanks for the help.
Well in that case, I'm not sure why this doesn't work properly:
$(document).ready(function(){
var activeOpacity = 1.0,
inactiveOpacity = 0.6,
fadeTime = 100,
clickedClass = "selected",
thumbs = ".thumbscontainer ul li img";
$(thumbs).fadeTo(1, inactiveOpacity);
$(thumbs).hover(
function(){
$(this).fadeTo(fadeTime, activeOpacity);
},
function(){
// Only fade out if the user hasn't clicked the thumb
if(!$(this).hasClass(clickedClass)) {
$(this).fadeTo(fadeTime, inactiveOpacity);
}
});
$(thumbs).click(function() {
// Remove selected class from any elements other than this
var previous = $(thumbs+'.'+clickedClass).eq();
var clicked = $(this);
if(clicked !== previous) {
previous.removeClass(clickedClass);
}
clicked.addClass(clickedClass).fadeTo(fadeTime, activeOpacity);
});
});
I see you're using jQuery (and have edited your question accordingly).
With jQuery, it's really easy to get a list of matching elements using CSS syntax:
var list = $('#parentId > .selected');
That gets a list of the direct children of the element with the ID "parentId" that have the class "selected". You can then do things with them, such as:
list.removeClass("selected");
Then add "selected" to the element you want to select.
Edit I think this should do it:
$(thumbs).click(function() {
// Remove selected class from any elements other than this
var clicked, previous;
clicked = $(this);
if (!clicked.hasClass(clickedClass)) {
previous = $(thumbs+'.'+clickedClass);
previous.removeClass(clickedClass).fadeTo(fadeTime, inactiveOpacity);
clicked.addClass(clickedClass).fadeTo(fadeTime, activeOpacity);
}
});
I'm assuming there that the "selected" class isn't necessary for the fade effect to look right.
Note how the above will completely ignore the click if the clicked element already has the class. If you don't want that, remove the hasClass check and add .not(clicked) to the end of the previous = $(thumbs+'.'+clickedClass) line, but I don't know what your fade in would do at that point if you've already done it once.
I'm not getting the hover stuff; I thought you wanted this to happen on click, not hover.
You should take a look at
jQuery.removeClass()
So the point would be to first iterate trough all the images and unset the classname and than set the class on the active one.
Use .closest('.jcarousel-clip') to get the parent div,
then find all the thumbnails and use .removeClass('selected').
Hi Andy it isn't clear your question, but I am going to try to help you.
I am trying to help, and my skills on javascript arent that good either, plus I am not sure if I undertood the question right, please, dont vote me down.
function focusme(){
document.getElementById("focusme").focus();
}
function changeToCurrent(obj){
var menucont = document.getElementById('menu');
var arrLink = menucont.getElementsByTagName('a');
for (var i = 0 ; i < arrLink.length; i++){
arrLink[i].className='';
}
obj.className = "current";
}
<div class="menu" id="menu" >
<a href='' id='focusme' onclick='changeToCurrent(this)'>link1</a>
<a href='' onclick='changeToCurrent(this)'>Once Only link2</a>
<a href='' onclick='changeToCurrent(this)'>link3</a>
<a href='' onclick='changeToCurrent(this)'>link4</a>
<a href='' onclick='changeToCurrent(this)'>link5</
link6
</div>
Hope it helps.

Categories

Resources