JavaScript add and remove class function on click - javascript

Basically I have this problem. I have an accordion that toggles one heading open at a time and I am having a problem adding in the open/close image that sits to the right of the heading.
I have it so far so that once you click a heading it removes the 'open' image and toggles another class for the 'close' image. Now I need to basically swap out these classes again so that if you toggle another heading it removes the other image and goes back to the original.
Here is the code I am using.
JavaScript
<SCRIPT>
$("#accordion > li").click(function () {
$("#accordian li").removeClass("faq-header");
$(this).addClass("faq-header2");
if (false == $(this).next().is(':visible')) {
$('#accordion > ul').slideUp(250);
$('#accordion > ul').addClass('faq-header');
$(this).removeClass("faq-header");
}
$(this).next().slideToggle(300);
});
$('#accordion > ul:eq(0)').show();
</SCRIPT>
CSS
#accordion {
list-style: none;
margin-left:-38px;
}
#accordion ul:eq {
background-image:url(../img/faq-open.gif);
background-repeat:no-repeat;
background-position:right;
padding-right:20px;
}
#accordion li{
display: block;
background-color: #FFF;
font-weight: bold;
cursor: pointer;
}
.faq-header {
text-align:left;
background-image:url(../img/faq-close.gif);
background-repeat:no-repeat;
background-position:right;
margin-right:20px;
}
.faq-header2 {
text-align:left;
background-image:url(../img/faq-open.gif);
background-repeat:no-repeat;
background-position:right;
margin-right:20px;
}
#accordion ul {
list-style: none;
display: none;
}
#accordion ul li{
font-weight: normal;
cursor: auto;
background-color: #fff;
border-bottom:1px solid #999;
margin-left:-38px !important;
}
I have removed one class and added another class as you can see $("#accordian li").removeClass("faq-header"); and added the following $(this).addClass("faq-header2");
But I need to now remove .faq-header2 and add back .faq-header after it is no longer the section selected. It doesn't seem too hard to me but i just can't figure out how to code it. Should be a basic if function I would think...

The jQuery UI accordion is a well proven cross browser widget, and does images on open and close (by default on the left, but one change of CSS will put them on the right)
as per here
if you don't won't to use it, I would persue an option with toggleClass, here
EDIT
Thanks for posting your HTML, I didn't necessarily mean the whole page, just the HTML for you accordion functionality, but hey thats cool
First point though, your HTML seems a bit heavy for just doing an accordion. Its also not entirely valid to put a ul inside a ul (they tend to go inside li, as in a drop down menu style). Further more it doesn't seem to be much point in all those ul and li as each ul only has one li anyway, just seems like a lot more tags than you would really need. ul and li tend to come with a lot of default styling (bullet points, margins, padding, indents etc), which can mean a lot more CSS than need to make them display how you want. I would have gone with a simpler structure, makes it easier to write your jQuery. A bit more like this
<div id="accordion">
<h3>First header</h3>
<div>First content</div>
<h3>Second header</h3>
<div>Second content</div>
</div>
Anyway, that was just a comment from my experience. To your problem at hand, this worked for me
$("#accordion > li").click(function () {
var self = $(this);
self.next('ul').slideToggle(300, function () {
if ($(this).is(':visible')) {
self.removeClass('faq-header').addClass('faq-header2')
}
else {
self.removeClass('faq-header2').addClass('faq-header')
}
});
self.siblings().removeClass('faq-header2').addClass('faq-header').next('ul').slideUp(250);
});
toggleClass, although useful, in your circumstance as to how you want classes to be added and removed, may not be as useful as I would have thought

I think that toggle() function is a bit not intuitive. I usually use my pwn method replaceClass() to do as you want:
void replaceClass(Object classList, String surrogateClass, String classToReplace);
defined as follow
function replaceClass(elementClassList, firstClass, secondClass) {
if(elementClassList.contains(firstClass)){
{
elementClassList.remove(firstClass);
elementClassList.add(secondClass);
}
}

Related

How to make this working Java-script more efficient

I've got a working jQuery script that runs ok meaning it serves its purpose.
The question is: how to make this script more efficient?
Currently the script becomes active the moment a user places the mouse over (hover) a certain HTML5 section-tag with an ID. At this moment the script removes the existing class named 'noDisplay' from a subordinate nav-tag containing a submenu list, hence content becomes visible to the user. This submenu list may be three to four levels deep. The submenus are held in classes (subMenu1, subMenu2, subMenu3, subMenu4, etc.).
The script is written to serve individually each of the given section IDs and its sublevel classes.
Basically the script interacts with the DOM by removing the class 'noDisplay' upon mouse hover and restores the same class upon mouse leave.
(Tried to give a clear explanation. If not please ask.)
Here is a JSfiddle: enter link description here
I hope someone can suggest a way to do this much more efficiently.
Possibly with more sections (#ID's) and subMenu-levels (a class per level).
Using the CSS properties 'display: none;' and 'display:block;' would be the simplest solution but this is not desired because a search-bot my decide to skip content flagged as invisible to the user or a screenreader. The class 'NoDisplay' in use here keeps content invisible to users and keeps its readability to screen readers (and thus to most of the search bots).
So basically the script function remains as is to remove and add the class 'noDisplay' upon hover.
The goal is to obtain a script that is more efficient that could use for instance variables for each section, instead of writing code for each new section and hence extending the current script.
//section1$("#section1 .NavUL1 .subMenu1").hover(function(){
$(".NavUL2").removeClass("noDisplay"); //display
},function(){
$(".NavUL2").addClass("noDisplay"); //no display
});
$("#section1").hover(function(){
$("#section1 .NavUL1").removeClass("noDisplay"); //display
},function(){
$("#section1 .NavUL1").addClass("noDisplay"); //no display
});
$("#section1 .NavUL1 .subMenu1").hover(function(){
$(".NavUL2").removeClass("noDisplay"); //display
},function(){
$(".NavUL2").addClass("noDisplay"); //no display
});
//#section2
$("#section2").hover(function(){
$("#section2 .NavUL1").removeClass("noDisplay"); //display
},function(){
$("#section2 .NavUL1").addClass("noDisplay"); //no display
});
$("#section2 .subMenu1").hover(function(){
$(".subMenu1 .NavUL2").removeClass("noDisplay"); //display
},function(){
$(".subMenu1 .NavUL2").addClass("noDisplay"); //no display
});
$("#section2 .subMenu2").hover(function(){
$(".subMenu2 .NavUL2").removeClass("noDisplay"); //display
},function(){
$(".subMenu2 .NavUL2").addClass("noDisplay"); //no display
});
$("#section2 .subMenu3").hover(function(){
$(".subMenu3 .NavUL2").removeClass("noDisplay"); //display
},function(){
$(".subMenu3 .NavUL2").addClass("noDisplay"); //no display
});
$("#section2 .subMenu4").hover(function(){
$(".subMenu4 .NavUL2").removeClass("noDisplay"); //display
},function(){
$(".subMenu4 .NavUL2").addClass("noDisplay"); //no display
});
My suggestion would be to create a new class, call it whatever but for demonstrative purposes we'll call it hover-class
Then it becomes simple:
$('.hover-class').hover(
function() { $(this).addClass('noDisplay'); },
function() { $(this).removeClass('noDisplay'); }
);
I'd recommend just using CSS, there shouldn't be a need for JS:
nav ul{
position: absolute;
border: 1px solid #444444;
box-shadow: 8px 8px 11px #222222;
background: #888;
padding: 0.5em 0.5em 0.5em 0em;
list-style-type: none;
margin-left: 15%;
display: none;
}
.sectionBox:hover nav > ul, nav li:hover > ul {
display: block;
}
This does away with all the IDs and classes while keeping the same effect. You html looks like this now (just a snippet):
<ul>
<li><h2>various whatever1</h2></li>
<li>link11</li>
<li>link12</li>
<li>link13</li>
<li>link14</li>
<li><h2>sub1</h2>
<ul>
<li>sub1-link11</li>
<li>sub1-link12</li>
<li>sub1-link13</li>
<li>sub1-link14</li>
</ul>
</li>
</ul>
Here it is working: http://jsfiddle.net/VGXNz/1/
Update:
If you want to use your original noDisplay styles then this would be the CSS:
nav ul{
position:absolute;
border: 0;
clip: rect(0 0 0 0);
width: 1px;
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
}
.sectionBox:hover nav > ul, nav li:hover > ul{
height: auto;
width: auto;
margin: 0 0 0 15%;
border:1px solid #444444;
box-shadow:8px 8px 11px #222222;
background:#888;
padding:0.5em 0.5em 0.5em 0em;
list-style-type:none;
clip: auto;
overflow: visible;
}
Here's a fiddle: http://jsfiddle.net/KKmVU/1/
why would you use js in the first place? Css is perfectly capable of handling hover states, and IMO you should always go for the css solution if there is one.
I made some quick (and dirty) changes to your fiddle:http://jsfiddle.net/3epRN/1/
I removed a bunch of classes and id's from the markup, removed all js, and tweaked the css a bit. The relevant css looks like this:
.sectionBox nav {
display: none;
}
.sectionBox:hover nav {
display: block;
position: absolute;
top: 90%;
left: 50px;
background-color:#646464;
z-index: 5;
}
.sectionBox nav ul ul {
display: none;
}
.sectionBox nav ul li {
position: relative;
}
.sectionBox nav ul li:hover ul {
display: block;
position: absolute;
top: 0;
left: 80%;
background-color:#646464;
z-index: 5;
}
Obviously this needs some finetuning, but I'm sure you get the idea...
edit
I must admit I missed the part about the display:none beeing a problem for you. I do have to say I disagree with your arguments as to why (it is used al over the net, and crawlers and screen readers are smart enough nowadays).
That beeing said, nothing prevents you to use the css styling you now use to hide content (by adding the noDisplay class) directly in your css where I used the display:none;, and countering it when you want to display content by adding the following in stead of an ordinary display:block:
height: auto;
width: auto;
clip: auto;
overflow: visible;
The result would be identical to your js solution. I updated my fiddle to demonstrate:
http://jsfiddle.net/3epRN/2/

slide bottom border in from left on hover

i have this css for my menu:
#menu {
display:inline;
float:right;
}
#menu > ul > li {
display:inline-block;
margin-right:20px;
min-width:70px;
}
#menu > li {
display:inline-block;
list-style:none;
margin-left:auto;
margin-right:auto;
}
#menu > li:hover {
color:#000000;
}
#menu li a {
display:block;
padding-top:25px;
border-top:4px solid #FFFFFF;
color: #FFFFFF;
text-decoration:none;
text-align: center;
}
#menu li a:hover {
border-color:#000000;
color:#000000;
}
i want to be able to make a bottom border (like the top one but on the bottom) slide in from the side on link hover
here is a fiddle: http://jsfiddle.net/2w6NB/
Position your element you want coming from the left to be
left: -200px; //or however much it takes to hide the element completely or partially
Then here is some sample code that you might be able to successfully use to model your functionality:
$( "#item" ).hover(function() {
$( "#item" ).stop().animate({
left: "-1" //shows item
}, 400);}, function() {
$( "#item" ).stop().animate({
left: "-160" //this determines how far back the item goes after hovering
}, 400);
});
Let me know if you have questions or if it works.
I believe this link will help you: Sliding with CSS and Affect Other Element on Hover
The goal here is to slide a line/boarder from an "overflow:hidden;" div using either CSS webkit transition or a javascript function. You cannot have this happen on the same object as the menu links, but you can set it so that there is a div directly underneath it that will let the bar slide in.
(An example of this is setting "right:200px;position:absolute;width:200px;border-top:solid black 5px;" to the inside object and the div surrounding it to "overflow:hidden;width:200px;". Then you use the transition on a css hover event or a javascript function to move over the object back into the div so that it can display.
I hope that helps!

CSS and Jquery: addClass not overwriting css

I have a script that adds a class to an element on hover.
Issue is the new, added class does not seem to 'overwrite' the existing css (even though on my style sheet the added class is listed below the existing css).
I cannot use removeClass on the element as there is no actual initial class styling the element.
The 'initial' styling that needs to be overwritten is:
#menu ul li ul li {
background-color: #ccc;
}
The class that needs to be added is:
.whitebg {
background-color: #fff;
}
My script is:
$('#menu ul li ul li').hover(
function() {
$(this).addClass('whitebg');
},
function () {
$(this).removeClass('whitebg');
}
);
Does anyone know a way I can fix this up?
Thanks!
id selectors take precedence over class selectors. You need !important
.whitebg {
background-color: #fff !important;
}
This is because of the specificity. you can use !important as the other posts suggested but
using !important in your CSS is a bad practice.
Use that as you last option.
Instead use two classes..
Make sure the inner most li has the default class to it..
$('#menu ul li ul li').addClass('default').hover(
function() {
$(this).addClass('whitebg').removeClass('default');
},
function () {
$(this).removeClass('whitebg').addClass('default');
}
);
Check Fiddle
Simply add the !important tag to the background-color in .whitebg class. That should fix your problem.
then, you need code this:
#menu ul li ul li {
background-color: #ccc;
}
#menu ul li ul li.whitebg {
background-color: #fff;
}
Only in this way, can we have a common parent class.
!important will be a compatibility problem.

Change div color on mouse over

I have a line of text (a link) within a div. I'd like the div color to change on mouse over the link. I tried various things without success. You can see my current code here: http://jsfiddle.net/Grek/D3TzM/ Note that I'm not necessarily looking for a jquery solution. Tks for your help
CSS
.source-title-box a{
color:#467FD9;
display:inline-block;
}
.source-title-box a:hover{
color:#666666;
}
.source-title-box hover{background:#cb2326;}
JS:
$('a').hover(function(){
$(this).parent().toggleClass('hover');
});​
you can select below a pseudo class like :hover. No need for javascript at all for this.
http://jsfiddle.net/7bFKq/
.source-title-box:hover{
background-color:#467FD9;
}
.source-title-box:hover a{
color:#FFFFFF;
}
​
If you must do it with a hover on a, you will need javascript.
http://jsfiddle.net/7wwdb/
$('a').hover(function(){
// .closest will get you to the div regardless of what might
// be in between. With .parent you get to the absolute parent, which
// in your case is a span
$(this).closest('.source-title-box').toggleClass('hover');
});​
css is basically the same, just :hover to .hover
.source-title-box.hover{
background-color:#467FD9;
}
.source-title-box.hover a{
color:#FFFFFF;
}
jsFiddle DEMO
Just look for the closest div, the immidiate .parent() was a <span> tag (which aren't automatically block elements by nature, unless you make them that way).
$('.activity-title a').on('mouseover', function () {
$(this).closest('div').toggleClass('hover');
});
$('.activity-title a').on('mouseout', function () {
$(this).closest('div').toggleClass('hover');
});
Changes this:
.source-title-box a
{
color:#467FD9;
display:block;
text-decoration:none;
}
to:
.source-title-box a
{
color:#467FD9;
display:block;
text-decoration:none;
padding:15px;
}
And this:
.source-title-box
{
color: #000;
background: #fff;
padding: 15px;
width: 200px;
position: relative;
margin-top:10px;
border: 1px dotted #666;
}
to:
.source-title-box
{
color:#000;
background:#fff;
width:230px;
position:relative;
margin-top:10px;
border:1px dotted #666;
}
DEMO
No JS required.
Keep the JavaScript you have, and add this CSS class:
.hover {
background-color: #f00;
}
http://jsfiddle.net/RLjvB/
Greg,
There are 2 points:
1) The jquery .hover() function expects two handlers as argument.
One for handlerin (mouse over) and one for handlerout (on mouse out). Giving only one argument uses the argument as an In-Out handler, i.e the same handler for both mouse events.
2) Make sure that the script that you have written (js) is included at the bottom of the page. ie, just before closing the "body" tag.
This is because : the html element may not be loading when the script executes.
...Your HTML Code...
<script>
$('a').hover(function(){
$(this).parent().toggleClass('hover');
});​
</script>
</body>
Hope this helps.

Strange behavior in IE when combining click handler with CSS sibling selector

I am trying to design a menu that is triggered by clicking a button. When the user clicks the button, a click handler runs which adds a class to the button, and a CSS rule using an sibling selector makes the menu visible. It works fine in all the browsers I tested except IE 7 and 8.
In IE 7 and 8, I am experiencing these problems:
Clicking the button toggles the class but the menu doesn't appear or disappear until I move the mouse around a little bit.
The menu doesn't work at all unless I have a CSS :hover declaration for children of the menu. It doesn't matter what I put in the declaration, or if I put anything at all, but the menu does not show up without it.
Can anyone tell me why this is happening and what I can do about it? I was thinking of adding a separate class to the menu but I am wondering if there is a simpler fix or workaround. Here is my code:
<!DOCTYPE html>
<html><head>
<title>IE selector test</title>
<style type="text/css">
button {
border: outset 1px #eeeeee;
}
button.active {
border-style: inset;
}
.menu {
display: none;
border: solid 1px #888888;
}
button.active ~ .menu {
display: block;
}
.menu > :hover {
/* For some reason, the menu doesn't work at all without this declaration */
}
</style>
</head>
<body>
<button type="button" id="menuButton">Menu</button>
<div class="menu">
<div>option</div>
</div>
<script>
document.getElementById("menuButton").onclick = function() {
if (this.className) {
this.className = "";
} else {
this.className = "active";
}
};
</script>
</body>
</html>
You can also test it at http://jsfiddle.net/QKqpn/.
You can work around it by forcing page redraw:
document.body.className = document.body.className;
See http://jsfiddle.net/uGW4M/
BTW, in your case you can use + (one immediate sibling) combinator instead of more common ~ (all subsequent siblings):
button.active + .menu {/* ... */}

Categories