Simplifying Active States - javascript

I have a question that I believe comes down to method preference.
In the following code when the div parent element is clicked on, the div itself expands and also triggers an icon animation
var slideSection6 = document.getElementById("manualsHandbooks").addEventListener("click", function()
{
if (this.parentElement.style.height == "60px" || this.parentElement.style.height == "")
{
this.parentElement.style.height = "215px";
document.getElementById("MAHB").classList.add("active");
}
else
{
this.parentElement.style.height = "60px";
document.getElementById("MAHB").classList.remove("active");
}
}, false);
I was thinking about how easy it was just to add a class and change the state of the icon and I wanted to experiment and try adding the another click event just to the icon to make it animate on a click and active the parent element in the div as well. Basically the reverse of the above.
I thought it would be as simple as adding another condition to the if statement to the effect of
|| document.getElementId("MAHB").classList.add=("active") == true
But this doesn't work and I know it's not proper form. Could someone get me started so I could figure this out?

Element.classList has a toggle method that could make things a bit easier. If you want to check if a certain class is present, you can also use classList.contains.
I'd suggest not using the height of your elements to determine their current state. Why don't you alter the height using an active css class?
Your click handler could then be:
var button = document.getElementById("manualsHandbooks");
button.addEventListener("click", function() {
this.parentElement.classList.toggle("active");
document.getElementById("MAHB").classList.toggle("active");
}, false);
You can read more about classList here: https://developer.mozilla.org/en-US/docs/Web/API/Element/classList

Related

Detecting when clicking anywhere outside of element when the clicked element has been removed from the DOM?

I have a notifications dropdown menu that should be closed when you click anywhere outside of it. The following code was working great until I ran into a new situation:
$(document).click(function(e) {
var target = e.target;
if (!$(target).is('.notification-area') && !$(target).parents().is('.notification-area')) {
$('.notification-area .flyout').removeClass('flyout-show');
}
});
However (and I'm using Backbone if that's relevant), some elements cause part of the menu to re-render. That is to say: remove and rebuild a part of the DOM.
Obviously you can't tell where an element is within the DOM if it's already been removed. So now, if there's a click that causes part of that view to re-render then that bit of code that checks the parents() of the element returns no parents.
Then I thought I might be able to solve it by checking if the length of parents() is greater than 0.
...
if (!$(target).is('.notification-area')
&& !$(target).parents().is('.notification-area')
&& $(target).parents().length > 0)
...
And this works but I wonder what side effects it could have. Is this the best way to do this?
Hope I understood your question correct. You want some simple way of not shutting the notification area if clicked upon it. But close it when clicked on body?
One way to do these kind of things is somewhat like this.
mouseOverArea = false; // This will be globally set, right away
$('.notification-area').mouseenter(function(){
mouseOverArea = true;
}).mouseleave(function(){
mouseOverArea = false;
});
And then when you click on body or whatever, you simply check if mouseOverArea == false... If so, close the notification box, otherwise return false, e.preventDefault(); or whatever fits your coding.
You can simplify this using closest() since it includes both the target and ancestors.
It turns :
!$(target).is('.notification-area') && !$(target).parents().is('.notification-area')
Into a simpler to read:
!$(target).closest('.notification-area').length
reference: closest() docs

Jquery Content Slider: Add an error class to a navigation menu that corresponds to empty section

I have a form that is broken up into a Jquery Content slider (i.e. vertical tabs).
This breaks the form into manageable chunks for the user to fill in.
At the moment, if the user forgets to fill in a field, an error class is applied to that section of the form. However, if that section is not visible, the user won't see the error styling.
I want to add the error class to the corresponding part of the vertical tab navigation menu. How can I do this?
The Jquery code that is used to flip between the sections is as follows:
$(".taptabs li").live('click', function() {
$(this).parent().parent().parent().$('.pages .page').hide().eq($(this).index()).show().addClass("animated").addClass(mode).addClass("fadeInLeft");
});
(Taptabs is the name of the navigation list. Each section of the form has a class of page Each section is also in the HTML element of <section> ).
I thought this would work to make the error message appear on the navigation:
$(".changing-room").submit(function () {
var isFormValid = true;
$("input.required").each( function () {
if ( $.trim( $(this).val() ).length === 0 ) {
$(this).parent().addClass("error");
var menulink = $(this).closest('section').index();
$('.tapnav li').index(menulink).addClass('error');
isFormValid = false;
}
});
});
In this code, when an Input is left empty, the closest Section tag is located (This section tag holds that particular section of the form). The index of that section tag is calculated and then it is applied to the corresponding LI of the Nav menu. However, it doesn't seem to work, it actually breaks all the code!
Here is a JS Fiddle
Two possibilities I guess. One is you don't let them go to the next tab until they finished their work on the current tab. That's option #1 below. Option #2 as you state is to style the tab somehow so they can see they need to return to it. See #2 below.
#1:
In your click event (use .on() not .live()) wrap the containing code in this:
if(!$('.error').length){
//All the code you have in your event now should be here - that is, there are no errors they can proceed.
} else {
//else is optional. Here you could do whatever you want to tell the user - "no, you can't move on because you have a validation issue. Here is a suggestion:
alert('You have errors! ' + someerrorvar);
}
#2
Again, as above use a .on() event instead of .live() because .live() is no more.
I'm going to make an assumption that there's a class that indicates a certain tab is the active tab. We'll call that class 'tater' for lack of a true class name. If so, just use that thusly:
//At the beginning of your event, before you pull the switch
$('.tater').addClass('incomplete');
Then use incomplete to style that tab as desired.
Final Thoughts:
I would go with #1. It's clear to the user that, hey, something's not right here and we need to fix it. Be sure and put the validation text clearly in the user's face.
I figured this out:
$(".MY-WRAPPER-CLASS").submit(function () {
var isFormValid = true;
$("input.required").each(function () {
if ($.trim($(this).val()).length === 0) {
$(this).parent().addClass("error");
var menulink = $(this).closest('section').index(); //Note 1
$('.tapnav li').eq(menulink).addClass('error'); //Note 2
isFormValid = false;
}
else {
$(this).parent().removeClass("error");
}
});
});
Note 1: We use .closest() to find the parent <section> tag of the empty form element.
We then use .index() to find the index value of the section.
It is then assigned to a variable called menu link.
Note 2: We then use .eq() to target the menu link that has the same index value as the section.

MouseOver/MouseOut EventListener inheriting to child nodes?

Edit: Solution
Thanks to Gaby for the help in finding a solution! Didn't quite work exactly how I wanted it to, found a better solution modified from the answers. What I do is only execute the mouseover/mouseout functions when the two elements (target and related target) do not share a parent.
Just modified Gaby's example a bit and have everything working great. As long as your popup is within the same div element as whatever spawns it (even if it's outside the main contents you can append it with overflow visible) and you don't go between non-shared elements on the way over to it, it'll stay alive.
divContents.addEventListener('mouseover', mouseEnter(showPopup, divContents));
divContents.addEventListener('mouseout', mouseEnter(hidePopup, divContents));
Now, the modified mouseEnter...
function mouseEnter(_fn, _parent) {
return function(_evt) {
if(!shareParent(_evt.target, _evt.relatedTarget, _parent)) {
_fn.call(this, _evt);
}
}
};
function shareParent(_object1, _object2, _parent) {
if (_object1 === _object2) {
return true;
}
while (_object1 && _object1 !== _parent) {
_object1 = _object1.parentNode;
}
while (_object2 && _object2 !== _parent) {
_object2 = _object2.parentNode;
}
return _object1 === _object2;
}
Solved my problem A-OK, because what I want to fire for mouseover and mouseout events will only happen when the elements don't share a parent - exactly how I intended on displaying the popups anyhow.
Thanks again for the code example from Gaby, though!
NOTE: No error checking for parent validity in shareParent function, haven't checked but it should always return true when it gets to the top of the tree (assuming the _parent isn't actually a parent of either _object1 or _object2). So make sure the parent object you pass to it is valid.
Original Question:
I'm having a problem in JavaScript right now.
I'm trying to create a div Element that pops up dynamically when something has a mouseover. The div is created directly adjacent to the object that spawns it.
divCreator.addEventListener('mouseover', createPopup, true);
divCreator.addEventListener('mouseout', hidePopup, true);
That creates the popup. Now, in the popup, when I create it, I run this before I append it to the document:
divPopup.addEventListener('mouseover', createPopup, true);
divPopup.addEventListener('mouseout', hidePopup, true);
This ensures that if I mouseover the popup, it keeps it alive (because the divCreator mouseout will fire) and when I mouseout of the popup it disappears.
However, with this method, whenever I mouseover a child element it detects a mouseout event from the parent element (divPopup) and closes the div.
Can I make the children 'transparent' to Events, so-to-speak?
There are two events that handle this case.
The mouseenter W3 DOM3 docs and mouseleave W3 DOM3 docs but they are currently in the DOM3 Events working draft.
They were introduced by Microsoft IE5.5, and Firefox has added support in v10.
A workaround is to manually check and cancel the execution of your handler if the newly moused-over element is a child of your top level one..
code adapted from http://blog.stchur.com/2007/03/15/mouseenter-and-mouseleave-events-for-firefox-and-other-non-ie-browsers/
divCreator.addEventListener('mouseover', mouseEnter(createPopup), true);
divCreator.addEventListener('mouseout', mouseEnter(hidePopup), true);
function mouseEnter(_fn)
{
return function(_evt)
{
var relTarget = _evt.relatedTarget;
if (this === relTarget || isAChildOf(this, relTarget))
{ return; }
_fn.call(this, _evt);
}
};
function isAChildOf(_parent, _child)
{
if (_parent === _child) { return false; }
while (_child && _child !== _parent)
{ _child = _child.parentNode; }
return _child === _parent;
}
Demo at http://jsfiddle.net/gaby/jMvHv/ (open your console for log messages)

Javascript onClick and onmouseover and onmouseOut

I would like to ask how to differentiate between onClick and onMouseOver and onMouseOut.
For instance,
I use onMouseOver to change the tab background to grey using
onMouseOver="this.style.backgroundColor=Blue;"
onMouseOut takes away this background
onMouseOut="this.style.backgroundColor=White;"
How do I write a call for onClick that gives a blue background and keeps it there even when the mouse cursor moves away from the tab?
Thanks
How about you have two CSS classes, "active" and "clicked". They both set the background to blue. On click, you add the "clicked" class. On mouse over, you add the "active" class. On mouse out, you remove the "active" class. If the element had the "clicked" class it'd still have it, and hence keep its color.
You need to work with event listeners. I recommend using a framework for this, like prototypejs or jquery.
You will need event listeners / observers for observing the mouseOver, mouseOut and click events. When your click event fires, you stop observing for mouseOver and mouseOut. This should do the trick.
I would do a check in the MouseOut that only changes the color if it's not already blue.
Normally I would add a CSS class to the element which has the active (last clicked on item) state. That CSS class can have priority over the normal styling of the element.
I would even use CSS and not JavaScript for hover state changes.
Very simple solution is here for your help. Hope it works for you.
<script type="text/javascript">
function row_mout(mout_value) {
document.getElementById(mout_value).style.background = "#FFFFFF";
}
function row_mover(mover_value){
document.getElementById(mover_value).style.background = "#123456";
}
function row_click(click_value) {
if(document.getElementById('chk_'+click_value).checked == true){
document.getElementById(click_value).style.background = "#123456";
document.getElementById(click_value).onmouseout = "";
document.getElementById(click_value).onmouseover = "";
}//if over
else if(document.getElementById('chk_'+click_value).checked == false){
document.getElementById(click_value).style.background = "#FFFFFF";
document.getElementById(click_value).onmouseout = function onmouseout(event){
row_mout(click_value);
}
document.getElementById(click_value).onmouseover = function onmouseover(event){
row_mover(click_value);
}
}//else if over
}

Making JQuery horizontal accordion close on click

Example: http://vincent-massaro.com/map/
Currently, the script allows you to click on a piece of the accordion to open it, but it is set to close on mouseleave. When I set the mouseleave to .click, it gets confused and doesn't know what state it is in. I want to make it so that you can click to open it, and click to close it, instead of mouseleave. The code controlling this is below, and the full script is in haccordion.js linked in the page source. If someone could help me modify this script, I would be very grateful! Thanks in advance.
$target.click(function(){
haccordion.expandli(config.accordionid, this)
config.$lastexpanded=$(this)
})
if (config.collapsecurrent){ //if previous content should be contracted when expanding current
$target.mouseleave(function(){
$(this).stop().animate({width:config.paneldimensions.peekw}, config.speed)
})
}
try use this
$('.accordion-item-header').click(function() {
var item = $(this).parent().find('.accordion-item-body');
item.toggleClass('open').animate({
width:item.hasClass('open') ? 0: 100
}, 100).toggleClass('open');
});
You could set a boolean variable to represent whether the accordion is open or not and just check it on click. (You'll need to toggle the variable's state on click too)
Edit:
Ok try this. Set a boolean global variable (outside the click event) like this:
var accordion_expanded = false;
Then within your click event do something like this: (I haven't tested this so you might have to massage it a bit to fit your circumstance)
In the function where you expand your accordion put this:
accordion_expanded = true;
And in the function where you contract your accordion do a
if(accordion_expanded == true){
//Contract accordion code goes here
accordion_expanded == false;
}
Good Luck!

Categories

Resources