javascript closure to remember initial state - javascript

I have a div in CSS that works like this: SomeDiv has another class, that's sometimes SomeRedDiv and other times SomeBlueDiv. When I mouseenter on SomeDiv, I want it to add the class SomeYellowDiv. But when I mouseleave, I want it each div to return to its initial state, either SomeRedDiv or SomeBlueDiv. This is what I have:
<div class="SomeDiv SomeRedDiv"></div>
<div class="SomeDiv SomeBlueDiv"></div>
$('.SomeDiv').mouseenter(function () {
// this makes all SomeDivs turn yellow
$(this).removeClass().addClass('SomeDiv SomeYellowDiv');
});
$('.SomeDiv').mouseleave(function () {
// here I want to use closure so that the function remembers
// which class it initially was; SomeBlueDiv or SomeRedDiv
$('this).removeClass().addClass('SomeDiv'); // add the initial color class
});
I could do this with a global but I want to see if a closure would make my code better; I know the concept of closure that allows functions to remember their state but I'm not sure how to make it work here.
Thanks for your suggestions.

Clsoures don't apply here, since you have two unrelated functions.
Instead, you should use $(this).data(...), which stores arbitrary data associated with an element.

There's no real need for closures here - you just need to push the red/blue class into some other data container on mouse enter, then reinstate it on mouse leave.
$('.SomeDiv').mouseenter(function () {
//remember the current colour class...
$(this).data('orig-colour', $(this).is('.SomeDivBlue') ? 'Blue' : 'Red'));
//...and now remove it and add yellow
$(this).removeClass('SomeDivRed SomeDivBlue').addClass('SomeYellowDiv');
});
$('.SomeDiv').mouseleave(function () {
//remove yellow and reinstate the original colour class
$(this).removeClass('SomeDivYellow').addClass('SomeDiv'+$(this).data('orig-colour'));
});
Note also I remove only the classes that need to be removed, as opposed to your code where you were removing all classes then re-adding as required.
You might also want to think about delegating the event if you have a lot of divs as this is more optimal performance wise. This isn't a big change;
$('.SomeDiv').mouseenter(...
becomes something like
$('body').on('mouseenter', '.SomeDiv', ...
Finally, I assume there is some programmatical reason as to why you physically need to remove a class. If the purpose is purely visual, at the risk of pointing out the obvious, you should craft your CSS so the yellow class merely overrides the effects of the blue/red class, alleviating the need to explicitly remove the latter.

Related

Loop through every object with a certain class, performing some css on all objects EXCEPT the object that triggered the event

What I wish to know is how would I modify the below code to apply css to all objects with a specific class EXCEPT the object which triggered the hover event.
$(".back").hover(
function(){
$(this).find("span").show();
$(this).find("span").slideDown("slow");
},
function(){
$(this).find("span").css ("display", "none");
}
);
So at the moment the above code will apply css to the hovered object containing the class '.back'. Whereas I want the opposite, so apply css to all objects with the class '.back' except the one triggering the event.
Now I know it would go along the lines of analysing the current object being looped over, and if that object != to the object triggering the hover then apply the css. But I am unsure of the syntax.
If this doesn't make sense, please don't hesitate to ask me to explain further.
Thanks for any help!
Callum
Use .not()
$(".back").hover(
function(){
$('.back').not(this).find("span").show();
$('.back').not(this).find("span").slideDown("slow");
},
function(){
$('.back').not(this).find("span").hide();
}
);

Should I declare more functions or pass switch arguments to one function in JavaScript?

My question involves the performance gain or loss when declaring and using functions with JavaScript. Suppose I have a function, foo(), defined as such:
function foo(arg1) {
//a simple CSS change, such as changing background color
}
Where arg1 is a String used to identify an HTML element. foo() is triggered on a mouseover event. The CSS change happens using jQuery, but I don't think that's important to my question. I would like to revert the CSS property back to default on a mouseout event. Would there be performance advantages of declaring another function, foo2(), such as:
function foo2(arg1) {
//undo the CSS change
}
Or would it be better to redefine foo() as follows, using a second argument as a switch:
function foo(arg1,arg2) {
if(arg2 == 'change') {
//make the CSS change for arg1
}else if(arg2 == 'revert') {
//revert the change for arg1
}
}
I am not concerned with the load time of the page. My goal is to minimize my amount of code, but without hampering the speed of the CSS change.
EDIT: arg1 will not strictly refer to one element. It can be used to identify a set of elements, such as a class name shared by <td>'s in a column. This is why the :hover CSS selector will not do what I need to do.
EDIT: Again, for clarity, suppose I have a set of elements containing the class arg1. I want ALL of the elements to experience the CSS change even when only ONE of the elements with that class name triggers the event.
You may want to consider using the CSS :hover psuedoselector instead of implementing this in javascript. To use your example of changing background color:
#yourElementID {
background-color: blue;
}
#yourElementID:hover {
background-color: green;
}
This will change the background color to green when the mouse is over the element, and back to blue when the mouse leaves.
As far as the best choice for code maintenance purposes, you would want to combine the two functions into a single function. This makes your code much more readable for people in the future.
As an added bonus, combining the functions leaves the possibility of very eloquent code such as switching the CSS back using a simple conditional or negation of a true to false value etc.
Robert C. Martin in Clean Code writes
FUNCTIONS SHOULD DO ONE THING. THEY SHOULD DO IT WELL. THEY SHOULD DO
IT ONLY.
He goes on to say
Flag arguments are ugly. Passing a boolean into a function is a truly
terrible practice. It immediately complicates the signature of the
method, loudly proclaiming that this function does more than one
thing. It does one thing if the flag is true and another if the flag
is false!
While your situation is not a boolean parameter, I interpret Martin's advice to apply to any parameter used for a branch to "do more than one thing".
Like many others have said, if you are just changing CSS-based on whether the mouse is hovering over an HTML element or not then you should probably just do that in the CSS with :hover:.
If you must use jQuery for some reason this is probably the best way to do it.
// Add event listener for mouseover to element(s)
$(".myHtmlElements").on("mouseover", function(){
// 'this' always refers to the element that triggered the event in jQuery
$(this).css("background-color", "#FF0000"); // Red
});
// Add event listener for mouseout to element(s)
$(".myHtmlElements").on("mouseout", function(){
// 'this' always refers to the element that triggered the event in jQuery
$(this).css("background-color", "#0000FF"); // Blue
});

how to reset elements affected by a jquery function

I have a jQuery function that runs when a box is clicked that affects multiple other elements on the page (creates a slideshow from a group of divs). There are multiple boxes on the page and I need to reset the elements affected by the function back to normal whenever the box is clicked again. Is there an easier way to reset the elements affected by the jQuery, or basically run the function backwards vs. writing another function that undoes what the first function did?
here is a jsfiddle.
There's no simple way to run the function backwards, because you need to know how to apply the opposite effect. Let's say you apply some color, you would first need to save the current color to be able to revert it.
The best way is to write a function that undoes what the first function did. Besides you can customize the "undo" function (suppose you want to do a different animation when scaling down an image or so).
One easy way to do what you describe is to use CSS classes to encapsulate all of the changes you want to apply with JQuery. Your first function uses addClass to apply the changes and then you can use removeClass to revert the changes.
Nope, there is no shortcut to this other than writing a custom function that does what you need.
Here's a discussion on the jQuery forum about this.
Well just a supplementary comment about your jsfiddle -- in your click listeners you should avoid create so many jquery objects using over and over again:
$(this)
Instead, define it just once at the top of the function:
var $this = $(this);
It will help performance and is a best practice.

jQuery Animation and Classes

Assume you have a list item, <li id="foo"> which you want to fade from one color to another when moused over, and that you are using jQuery. This is fairly easy:
$('li#foo').bind('mouseenter' , function(e) {
$(this).animate({backgroundColor: '#F00'} , 300);
});
However, what if you wanted to get the resulting color or other style rules from a class defined in CSS without also declaring them in JavaScript? It seems there's no way to learn style information from CSS rules without having an example of the rule already in the document, which would require you to animate the <li> to the target appearance, then in the animation-finished callback, set the class which leads to redundant style declarations and can foul up your CSS at "runtime".
Sorry if this question's unclear: It doesn't occur in the context of any specific project, I'm just curious how you'd go about this. Also, I know CSS3 hypothetically includes support for such transitions but using CSS for dynamic behavior like this seems such an ugly hack.
I'm pretty sure javascript can't read your style-sheet.
If you want a certain property from the style-sheet that does not occur anywhere on the page, you will have to add an invisible element that has that style applied, either at page-load time in the html or with javascript whenever you want.
It does seem a bit theoretical though, instead of defining styles in your style-sheet that you are not using, you might as well declare the appropriate variable directly in javascript.
You need the color plugin to animate background color.
You should be able to simply do:
$(this).css('background-color')
If you want to get the color AFTER its been updated, add it to the callback, like:
$('li#foo').bind('mouseenter' , function(e) {
$(this).animate({backgroundColor: '#F00'} , 300,function(){
alert($(this).css('background-color'));
});
});
You can then save that to a var, or do whatever you wanted to do with it. You could also change your "bind" to "live" and it will update automatically each time its run:
$(selector).live(event,function(){
alert($(this).css('background-color'));
});
As a side note, you shouldnt do li#foo :) just do #foo speeds up your selection time and its unnecessary as there is only 1 element with that ID.
I've played around a little with Calvin's idea, and this is what I got:
Assuming the following CSS:
#somediv .bar em {
color: #080;
}
You can create the elements virtually and get the style information that way:
$('<div id="somediv"><span class="bar"><em>')
.find('em').css('color')
Watch it in action.

Simple Horizontal Javascript Navigation with Prototype

I'm trying to implement a simple horizontal navigation menu that just shows a single div for each link. It is kinda like a dropdown menu but instead of a mouseover triggering a dropdown, an onclick event will trigger the showing of a div. I want to make sure I am taking the right approach before going too much further, any help is appreciated. This is what I have so far:
<ul id="settings_nav">
<li>
<a>Theme</a>
<div id="settings_block"><%= render :partial => 'email_password' %></div>
</li>
<li>
Lists
<div id="settings_block"><%= render :partial => 'lists' %></div>
</li>
</ul>
window.onload = function(){
settingsMenuInit('settings_nav')
}
function settingsMenuInit(settings_nav){
$(settings_nav).childElements().each(
function(node){
node.onclick= function(){ this.next.show() };
})
}
Something like that, but I am unsure how to get the div that is currently shown and hide it. I could iterate through all the childElements and hide each div and then show the one that is being clicked, but maybe there's a better way?
Some notes FW(T)W:
With Prototype and similar libraries, you don't want to hook up event handlers by assigning functions to the element's onclick and similar properties; that style has several disadvantages (not least that there can only be one handler for the event on the element). Instead, use Prototype's observe function:
someElement.observe('click', functionRefHere);
// or
Element.observe(someElementOrID, 'click', functionRefHere);
This also lets Prototype work around some IE memory loss bugs for you.
You might look at is Prototype's dom:loaded event, which happens sooner than window.onload (which won't happen until all of your images and other external resources have loaded, which can be a second or two after the page is displayed):
document.observe('dom:loaded', initFunctionRefHere);
You can use event delegation and just watch your settings_nav element, rather than each child node individually.
$(settings_nav).observe('click', handleNavClick);
function handleNavClick(event) {
var elm = event.findElement("some CSS selector here");
if (elm) {
event.stop();
// Handle it
}
}
As you can see, Event#findElement accepts a CSS selector. It starts with the actual element that was clicked and tries to match that with the selector; if it matches, it returns the element, otherwise it goes to the parent to see if it matches; etc. So with your HTML you might look for a li (event.findElement('li')) or the link (event.findElement('a')).
But if you want to watch each one individually, they can share a function (as they do in your example):
$(settings_nav).childElements().invoke('observe', 'click', handleNavClick);
function handleNavClick(event) {
// Prototype makes `this` reference the element being observed, so
// `this` will be the `li` element in this example.
}
Whether you watch each element individually or use event delegation depends on what you're doing (and personal preference). Whenever anything is likely to change (adding and removing navigation li elements, for instance) or when there are lots of things to watch, look to event delegation -- it's much easier simpler to deal with changing sets of elements using event delegation and just watching the parent. When dealing with a stable structure of just a few things (as in your example), it may be simpler to just watch the elements individually.
Once inside your handler, you can use Element#down to find child elements (so from the li, you might use li.down('div') to find the div), or Element#next to get to the next sibling element (e.g., going from the link to the div). Either way, once you have a reference to the div, you can use Element#show and Element#hide (or Element#toggle).
I recommend using named functions instead of anonymous ones (see my example above). Named functions help your tools (debuggers, browsers showing errors, etc.) help you. Just be sure not to declare a named function and use it as an expression (e.g., don't immediately assign it to something):
// Don't do this because of browser implementation bugs:
someElement.observe('click', function elementClickHandler(event) {
// ...
});
// Do this instead:
someElement.observe('click', elementClickHandler);
function elementClickHandler(event) {
// ...
}
...because although you should be able to do that according to the spec, in reality various bugs in various browsers make it not work reliably (article).

Categories

Resources