I am working on homework that involves working with javascript. Part of my homework assignment is to use the event handlers onmouseout and onmouseouver. What is supposed to happen when the user hovers over a specific div element, the font size grows by 25%, and when the user mouses out of the div element, the font size goes back to normal. My question is, is it possible to incorporate both an onmouseover function and an onmouseout function into one function? Somehow that is what my teacher wants us to do. I have this started so far.
function FontSize(x)
{
x.style.fonstSize = large;
}
I'm also thinking this isnt the correct code to make the font 25% larger, but I'm not sure how to really incorporate an onmouseout in this function.
As a teacher myself, I am 99% sure that by "one function" the instructor means one general-purpose function to change the font size, not one function which uses conditional statements to work backwards and figure out whether it should be doing onmouseout or onmouseover.
Your script should contain:
function resize(elem, percent) { elem.style.fontSize = percent; }
Your HTML should contain:
<div onmouseover="resize(this, '125%')" onmouseout="resize(this, '100%')"
Text within div..
</div>
Note: Situations such as here, are exactly why JavaScript has the keyword "this"--to save us from needing to use complicated document.getElementById() statements.
You can use "%" property for controlling font-size as described here with the following code.
document.getElementById("div1").onmouseover = function() {
document.getElementById("div1").style.fontSize = "125%"
};
document.getElementById("div1").onmouseout = function() {
document.getElementById("div1").style.fontSize = "100%";
};
Here is the working jsfiddle : http://jsfiddle.net/LxhdU/
Yes you can. Call the same function on both events, and pass a parameter to indicate whether the fontsize should increase or decrease.
ChangeFontSize = function(element, shouldIncreaseFontsize)
{
var small=14;
var large = small * 1.25;
if(shouldIncreaseFontsize) {
element.style.fontSize = large + "px";
}
else {
element.style.fontSize = small + "px";
}
}
http://jsfiddle.net/TMHbW/1/
I'd do something simple like the following. The large and small values can be whatever you need them to be for the font size to work or they can be variables you've defined in prior code.
Demo: http://jsfiddle.net/lucuma/EAbYn/
function doHover(e) {
if (e.type=='mouseover') {
this.style.fontSize = "large";
} else {
this.style.fontSize = "small";
}
}
var el = document.getElementById('myelement')
el.onmouseout =doHover;
el.onmouseover=doHover;
It is possible you do not need to call both the events on the element explicitly instead extension you create will do that.Extend the Element's prototype. Jquery also does similar to this.
Ref Prototype
See Fiddle:- http://jsfiddle.net/4fs7V/
Element.prototype.hover= function( fnOver, fnOut ) {
this.onmouseover=fnOver;
this.onmouseout=fnOut || fnOver;
return this;
};
document.getElementById('test').hover(function(){
//do your mouseover stuff
},
function(){
//do your mouseout stuff
});
Update
Same can be achieved with just one function too:-
Hover me
.largeFont {
font-size:125%;
}
Element.prototype.hover = function (fnOver, fnOut) {
this.onmouseover = fnOver;
this.onmouseout = fnOut || fnOver;
return this;
};
document.getElementById('test').hover(changeMe);
function changeMe()
{
if(this.hasAttribute('class'))
{
this.removeAttribute('class');
}
else
{
this.setAttribute('class', 'largeFont');
}
}
Related
I'm trying to call a function based on screen size inside a loop'.
The function works fine when the page is loaded, however, the content doesn't change when the window is resized.
Here's an example:
https://jsfiddle.net/celine305/BaNRq/27/
Any help would be much appreciated.
for (var i = 0; i < 2; i++) {
function red() {
$('#' + i + '').css('background', '#B60C0C')
.text('Screen Size RED');
}
function orange() {
$('#' + i + '').css('background', '#EBAE10')
.text('Screen Size ORANGE');
}
function green() {
$('#' + i + '').css('background', '#83ba2b')
.text('Screen Size GREEN');
}
var widths = [0, 500, 850];
function resizeFn() {
if (window.innerWidth >= widths[0] && window.innerWidth < widths[1]) {
red();
} else if (window.innerWidth >= widths[1] && window.innerWidth < widths[2]) {
orange();
} else {
green();
}
}
resizeFn();
window.onresize = resizeFn();
}
Move your functions outside of the for loop
Merge 3 functions to one
Use a jQuery listener instead of JavaScript since you're using jQuery already
// You could also assign classes and use $(".className") instead of loop
function changeColor(color) {
for(var i=0; i<2; i++){
$('#'+i+'').css('background',color).text('Screen Size' + color);
}
}
var widths = [0, 500, 850];
function resizeFn() {
if (window.innerWidth>=widths[0] &&window.innerWidth<widths[1]) {
changeColor('#B60C0C');
} else if (window.innerWidth>=widths[1] && window.innerWidth<widths[2]) {
changeColor('#EBAE10');
} else {
changeColor('#83ba2b');
}
}
resizeFn();
$(window).resize(function() {
console.log("resize");
resizeFn();
})
JSFiddle renders the content inside a div so your code was never detecting the resize. I added a container to detect the resize, but otherwise you should use $(window).
Fiddle: https://jsfiddle.net/BaNRq/29/
The whole point of your loop seems to be selecting the elements. You can do this much simpler by selecting all your elements with one query:
$("#1, #2")
Because you're doing EVERYTHING in the loop, you are redefining functions and are assigning the resize-function multiple times.
Oh, and by the way, you are not really assigning a function to the window.onresize event handler, but the return value of a function. Try assigning it without the trailing braces.
If you want to use the current code, you will likely have to move the function declarations outside of the for loop. See this article.
As a side note though, I would recommend moving away from javascript for handeling these style changes. CSS offers a good way to handle style changes for predetermined sizes in the form of media queries.
I'm having toruble with this function, it requires two clicks before the if statement is satisfied even though in the CSS the condition should be met. On the fist click, the console shows triggered but not if state on the second click it does show if state can anyone understand why the condition is not being met?
function searchShow() {
console.log('started');
document.getElementById('top_line_2a').addEventListener('click', function() {
console.log('triggered')
var searchClickIcon = document.getElementById('top_line_2a');
var searchClick = document.getElementById('top_line_3');
if(searchClick.style.height == '0em') {
console.log('if state');
//searchClick.style.display = 'block';
searchClick.style.height = '3em';
searchClickIcon.style.color = 'white';
searchClickIcon.style.textShadow = '0px 0px 7px white';
document.getElementsByClassName('search')[0].focus();
} else {
//searchClick.style.display = 'none';
searchClick.style.height = '0em';
searchClickIcon.style.color = 'rgba(255, 187, 61, 1)';
searchClickIcon.style.textShadow = '';
}
})
console.log('added');
}
When implementing ping-pong / toggle effects, try not to compare with attribute value directly. The zero height could be "0em", "0", or numeric 0. You could try normalizing the value for this one particular case:
if (parseInt(searchClick.style.height,10)==0) {
// show the container
} else {
// hide the container
}
A much more reliable way is to take advantage of the fact that every DOM element can be dynamically assigned new attributes. Since you already have a handle to the searchClick object:
if (searchClick.showing){
searchClick.showing=null;
// hide the container
} else {
searchClick.showing=true;
// show the container
}
"showing" is your own attribute. When you first click on it, the marker is not there, so it'll show the container (initially hidden). Then the showing flag is attached to it, so you can detect it in the next click. If your initial state is showing, then use a different flag to reverse the logic. This is a sure fire method to implement a toggle.
You shouldn't be using the height. Use a variable instead.
var triggered = false;
function searchShow() {
document.getElementById('top_line_2a').addEventListener('click', function() {
//Do stuff
if(!triggered) {
triggered = true;
//Do stuff
} else {
triggered = false;
//Do stuff
}
})
}
Checking for a style in an if statement isn't a good practice. If you ever change the size of your container for X reason, you'll also have to change the if/else statement to fit the change. It also make the code that much less clear to whoever will read it. Always try to avoid using hardcoded numbers when you can use something more effective.
I really have trouble with OO coding in js. I have written a piece of code which rotates through 3 divs, and pauses on hover of any div. This code is just regular js using an array/json as the input. the code is a bit long so sorry about that. I just need some guidance on how I can convert this primitive code to a better form, as in OO and encap. When I tried myself I could not pass the slides array/json to my defined object. Is there a trick or guideline i can follow on how to rewrite this to a better form?
Edit - What is a good guideline to follow so I can rewrite this with objects instead of global variables and loose functions
var slideIndex = 0;
var prevIndex = 0;
var t;
function initPromo(){
sortSlides();
nextPromo();
addListeners();
}
function addListeners(){
for(var i=0; i<slides.length; i++)
$(slides[i].el).hover(function(){ stopPromo(); }, function(){ resumePromo(); });
}
function resumePromo(){ startTimer(); }
function stopPromo(){ clearTimeout(t); }
function nextPromo(){
if(slideIndex > 0 || prevIndex > 0) $(slides[prevIndex].el).css("display","none");
$(slides[slideIndex].el).css("display","block");
prevIndex = slideIndex;
slideIndex = (slideIndex<slides.length-1) ? slideIndex+1 : 0;
startTimer();
}
function startTimer(){ t = setTimeout("nextPromo()", 3000); }
function SortByWeight(a,b) { return b.weight - a.weight; }
function SortByWeightFr(a,b) { return b.frWeight - a.frWeight; }
function sortSlides(){
($("body.en").length > 0) ? slides.sort(SortByWeight) : slides.sort(SortByWeightFr);
}
var slides = [
{
el:'#ps1',
weight:1,
frWeight:3
},
{
el:'#ps2',
weight:0.5,
frWeight:6
},
{
el:'#ps3',
weight:4,
frWeight:9
}
];
window.onload = function () {
initPromo();
};
HTML
<body class="en">
<div id="homepageSlides">
<div id="promoSlides">
<div id="ps1">ps1</div><div id="ps2">ps2</div><div id="ps3">ps3</div>
</div>
</div>
</body>
Edit: Early days in OO coding, not asked in the right way
Well your "plain javascript" code is already taking you part way there. The first function you have defined identies the domain object: Promo.
var Promo = function () { };
You have actions on an instance of promo, init, start, stop, resume, etc. These can be defined on the prototype of Promo.
Promo.prototype.init = function() {
// ...
};
It could get a little annoying typing prototype each time, so we could bundle the prototype into a pointer that allows us a lot easier access...
var Promo = function () { };
(function(obj) {
obj.init = function() {
// ...
};
})(Promo.prototype);
So we've got some structure but we need to now separate concerns. Throughout your plain javascript you've got config type data strewn through the code. It's generally a good idea to isolate these bits of data to a single entry point for your object.
obj.init = function(_el) {
// where _el is the base element of this widget
};
I see you're also using jQuery which is good because it gives you a lot of power. One convention I like to use is instead of passing a huge amount of config data into a given widget, I like to give my objects minimal config and let them inspect the HTML to determine additional configuration data. This has the added advantage of if you wanted to add slides in the future or otherwise make changes to the slide content you need'nt worry about changing the JS.
Let's say we were to alter the slide HTML to look like...
<div id="promoSlides">
<div data-type="slide" data-slide-id="1">ps1</div>
<div data-type="slide" data-slide-id="2">ps2</div>
<div data-type="slide" data-slide-id="3">ps3</div>
</div>
Using jQuery we could identify how many slides are present.
obj.init = function(_el) {
this.baseElement = $(_el);
this.slides = this.baseElement.find('*[data-type="slide"]');
};
Now we're passing in minimal config, we've separated out the identification of the slides to the HTML, and we've got a nice pattern for a self-sufficient object. The rest would be to fill in the details (totally untested, but something like this)...
var Promo = function () { };
(function (obj) {
obj.init = function(_el, _delay) {
// Initialize markup
this.baseElement = $(_el);
this.slides = this.baseElement.find('*[data-type="slide"]');
this.slideDelay = _delay;
// Sort slides
// (not sure what's going on here)
// Bind events
this.baseElement
.on('mouseenter', this.stop.bind(this))
.on('mouseleave', this.start.bind(this));
};
obj.start = function() {
this.timer = setInterval(this.advance.bind(this), this.slideDelay);
};
obj.stop = function() {
clearInterval(this.timer);
};
obj.advance = function() {
// Slide the visible slide off screen
// (note: the parent tag will need overflow:hidden)
var visible = this.baseElement.find('*[data-type="slide"]:visible');
visible.animate({ left: '-' + (visible.width()) + 'px' }, 1000);
// Slide the next slide in
var next = visible.next();
next.css('left', this.baseElement.width() + 1).animate({ left: '0' }, 1000);
};
})(Promo.prototype);
Note that I made use of bind which isn't supported yet in older versions of IE.
Its not the converting to object oriented style what is needed for that code there.
Here are issues i see there:
pollution of global scope
mixing fixed CSS rules with Javascript
use of .length attribute within a loop
no event delegation
misplacement of <script> tag, resulting in use of window.onload
creating new jQuery object when it is not needed
use of CSS3 selectors in jQuery calls
no clue how to use setTimeout()
tight coupling to HTML ( id on each slide )
I want to be able to do a cross fade transition on large images whose width is set to 100% of the screen. I have a working example of what I want to accomplish. However, when I test it out on various browsers and various computers I don't get a buttery-smooth transition everywhere.
See demo on jsFiddle: http://jsfiddle.net/vrD2C/
See on Amazon S3: http://imagefader.s3.amazonaws.com/index.htm
I want to know how to improve the performance. Here's the function that actually does the image swap:
function swapImage(oldImg, newImg) {
newImg.css({
"display": "block",
"z-index": 2,
"opacity": 0
})
.removeClass("shadow")
.animate({ "opacity": 1 }, 500, function () {
if (oldImg) {
oldImg.hide();
}
newImg.addClass("shadow").css("z-index", 1);
});
}
Is using jQuery animate() to change the opacity a bad way to go?
You might want to look into CSS3 Transitions, as the browser might be able to optimize that better than Javascript directly setting the attributes in a loop. This seems to be a pretty good start for it:
http://robertnyman.com/2010/04/27/using-css3-transitions-to-create-rich-effects/
I'm not sure if this will help optimize your performance as I am currently using IE9 on an amped up machine and even if I put the browser into IE7 or 8 document mode, the JavaScript doesn't falter with your current code. However, you might consider making the following optimizations to the code.
Unclutter the contents of the main photo stage by placing all your photos in a hidden container you could give an id of "queue" or something similar, making the DOM do the work of storing and ordering the images you are not currently displaying for you. This will also leave the browser only working with two visible images at any given time, giving it less to consider as far as stacking context, positioning, and so on.
Rewrite the code to use an event trigger and bind the fade-in handling to the event, calling the first image in the queue's event once the current transition is complete. I find this method is more well-behaved for cycling animation than some timeout-managed scripts. An example of how to do this follows:
// Bind a custom event to each image called "transition"
$("#queue img").bind("transition", function() {
$(this)
// Hide the image
.hide()
// Move it to the visible stage
.appendTo("#photos")
// Delay the upcoming animation by the desired value
.delay(2500)
// Slowly fade the image in
.fadeIn("slow", function() {
// Animation callback
$(this)
// Add a shadow class to this image
.addClass("shadow")
// Select the replaced image
.siblings("img")
// Remove its shadow class
.removeClass("shadow")
// Move it to the back of the image queue container
.appendTo("#queue");
// Trigger the transition event on the next image in the queue
$("#queue img:first").trigger("transition");
});
}).first().addClass("shadow").trigger("transition"); // Fire the initial event
Try this working demo in your problem browsers and let me know if the performance is still poor.
I had the same problem too. I just preloaded my images and the transitions became smooth again.
The point is that IE is not W3C compliant, but +1 with ctcherry as using css is the most efficient way for smooth transitions.
Then there are the javascript coded solutions, either using js straight (but need some efforts are needed to comply with W3C Vs browsers), or using libs like JQuery or Mootools.
Here is a good javascript coded example (See demo online) compliant to your needs :
var Fondu = function(classe_img){
this.classe_img = classe_img;
this.courant = 0;
this.coeff = 100;
this.collection = this.getImages();
this.collection[0].style.zIndex = 100;
this.total = this.collection.length - 1;
this.encours = false;
}
Fondu.prototype.getImages = function(){
var tmp = [];
if(document.getElementsByClassName){
tmp = document.getElementsByClassName(this.classe_img);
}
else{
var i=0;
while(document.getElementsByTagName('*')[i]){
if(document.getElementsByTagName('*')[i].className.indexOf(this.classe_img) > -1){
tmp.push(document.getElementsByTagName('*')[i]);
}
i++;
}
}
var j=tmp.length;
while(j--){
if(tmp[j].filters){
tmp[j].style.width = tmp[j].style.width || tmp[j].offsetWidth+'px';
tmp[j].style.filter = 'alpha(opacity=100)';
tmp[j].opaque = tmp[j].filters[0];
this.coeff = 1;
}
else{
tmp[j].opaque = tmp[j].style;
}
}
return tmp;
}
Fondu.prototype.change = function(sens){
if(this.encours){
return false;
}
var prevObj = this.collection[this.courant];
this.encours = true;
if(sens){
this.courant++;
if(this.courant>this.total){
this.courant = 0;
}
}
else{
this.courant--;
if(this.courant<0){
this.courant = this.total;
}
}
var nextObj = this.collection[this.courant];
nextObj.style.zIndex = 50;
var tmpOp = 100;
var that = this;
var timer = setInterval(function(){
if(tmpOp<0){
clearInterval(timer);
timer = null;
prevObj.opaque.opacity = 0;
nextObj.style.zIndex = 100;
prevObj.style.zIndex = 0;
prevObj.opaque.opacity = 100 / that.coeff;
that.encours = false;
}
else{
prevObj.opaque.opacity = tmpOp / that.coeff;
tmpOp -= 5;
}
}, 25);
}
I want that when mouse is over an image, an event should be triggered ONCE, and it should be triggered again only after mouse is out of that image and back again, and also at least 2 seconds passed.
My current function is called continuously (refreshcash) if I leave the mouse over my image
<img src="images/reficon.png" onmouseover="refreshcash()" onmouseout="normalimg()" id="cashrefresh"/>
function refreshcash() {
$("#showname").load('./includes/do_name.inc.php');
$("#cashrefresh").attr("src","images/reficonani.gif");
}
function normalimg() {
$("#cashrefresh").attr("src","images/reficon.png");
}
code update
This code seems to have a bug,but the algorithm is kinda logical
<script type="text/javascript">
var canhover = 1;
var timeok = 1;
function redotimeok() {
timeok = 1;
}
//
function onmenter()
{
if (canhover == 1 && timeok == 1)
{
enter();
canhover = 0;
}
}
//
function onmleave()
{
leave();
canhover = 1;
setTimeout(redotimeok(), 2000);
leave();
}
//
$('#cashrefresh').hover(onmenter(),onmleave());
function enter(){
$("#showname").load('./includes/do_name.inc.php');
$("#cashrefresh").attr("src","images/reficonani.gif");
}
function leave(){
$("#cashrefresh").attr("src","images/reficon.png");
}
</script>
Try the hover:
$('#cashrefresh').hover(function(){
$("#showname").load('./includes/do_name.inc.php');
$("#cashrefresh").attr("src","images/reficonani.gif");
}, function(){
$("#cashrefresh").attr("src","images/reficon.png");
});
And your image should look like:
<img src="images/reficon.png" id="cashrefresh"/>
Update:
Modify your code like this:
var e = null;
var l = null;
$('#cashrefresh').hover(function(){
e = setTimeout(enter, 2000)
}, function(){
l = setTimeout(leave, 2000)
});
function enter(){
$("#showname").load('./includes/do_name.inc.php');
$("#cashrefresh").attr("src","images/reficonani.gif");
clearTimeout(e);
}
function leave(){
$("#cashrefresh").attr("src","images/reficon.png");
clearTimeout(l);
}
Do you have the images cached in some way? If you replace them by their src attribute without specifying width/height elsewhere (best would be CSS) or having them readily available then the hovered box (img element) will collapse into a smaller (or no) box until the image has been loaded far enough for the browser to know the correct dimensions of the image to resize the box (which may affect other elements being adjusted to the image). The exact effect depends on the browser but you may lose the hover state causing the call of your mouseout function.
I assume that both images are the same size, so if you didn't already, you could try adding the dimensions to your CSS for #cashrefresh and see if that fixes the problem.
For the delay I would recommend using the jQuery timers plugin (or a similar one) which eases handling of timers compared to doing it on your own. You would probably want to give your timers names and try to stop older ones before you add the next one.