Hidden element with transition sometimes not transitioning? - javascript

I have the following to get JSON after clicking a submit button, where temperature is the div wrapper for the other listed elements:
$.getJSON("lookup.php?id=" + v, function(data) {
var items = {};
$.each(data, function(key, val) {
items[key] = parseFloat(val.replace(/\$|,/g, '')).toFixed(2);
});
console.log(items);
if (items["goal"] != null) {
$('#progress').on('webkitTransitionEnd transitionend', function(e) {
console.log(e);
});
$("#goal").text("Goal: " + "$" + items["goal"]);
$("#temperature").css("display", "block");
$("#progress").css("width", items["total"] / items["goal"] * 100 + "%");
$("#pct").text(items["total"] / items["goal"] * 100 + "%");
$("#raised").text("$" + items["total"]);
}
});
This code is inside a jQuery getJSON function. And the temperature should animate like a fundraiser temperature bar.
Here's what I have for that element's CSS:
#progress {
float: left;
transition: width 6s;
width: 0%;
height: 20px;
background: #FF5D50;
z-index: 333;
}
I've been getting inconsistent results with the transitions (I am aware that I should be adding the other browser CSS transitions as well). I'm using Chrome. Sometimes it loads and the transition happens, and sometimes it just loads directly to the percent it's supposed to be at. It should be animating every time, and without making changes to the code, I get different results like it's a race condition, and so it's very difficult to reproduce consistently. However, I read from other answers here on SO that DOM manipulation is synchronous. So I can't understand why sometimes it works and doesn't.
I would appreciate any explanation to what's happening here. Maybe what's checking to see if there needs to be a transition applied is being triggered sometimes before the display attribute changes and this is why it will sometimes not transition? I don't know what exactly checks if a DOM element is changed and applies the CSS transition.
Update: It works even with a 0ms setTimeout that encapsulates the progress bar update after changing the wrapper to display as block - can't reproduce on fiddle since it looks like it runs slower there.

Related

Changing attribute interupts other functions

Change of an attribute src seems to interupt other functions, even those not connected to the img. They don't work properly although when the line of code changing the attribute isn't called everything works perfectly fine.
I have my scripts.js that handles navigation - whenever you click header it should fade in content, change it and fade it back onto the screen.
The html of content that is to be changed:
<div id="photoContainer">
<div id="photoSection">
<img id="showedPhoto" src="img/photo1.png" />
</div>
</div>
Stylesheet:
#photoContainer
{
position: absolute;
top: 18%;
z-index: -1;
}
#photoSection
{
transition: all 0.4s ease-in-out;
}
And the most important, script:
/*
* 1. Hides photo after 0.2s of pressing button
* 2. Changes photo to another one
* 3. Shows photo
*/
function SwipePhoto(newSectionNo)
{
if(newSectionNo != currentSectionNo)
{
currentSectionNo = newSectionNo;
setTimeout(HidePhoto, 200);
setTimeout(HideContent, 200);
setTimeout(function() { changeContent(newSectionNo) }, 600);
setTimeout(ShowPhoto, 1000);
setTimeout(ShowContent, 1000);
}
}
HidePhoto, HideContent, ShowPhoto & ShowContent are just basic fadeIn/Out functions but the keypoint is the changeContent:
function changeContent(photoNo)
{
document.getElementById("showedPhoto").src = "img/photo" + photoNo + ".png";//THIS ONE
//showedPhoto.attr("src", "img/photo" + photoNo + ".png"); SAME SITUATION OCCUR WHILE USING JQUERY
content.html(text[photoNo - 1]);
sectionHeader.html(header[photoNo - 1]);
}
Without the line of code: document.getElementById("showedPhoto").src = "img/photo" + photoNo + ".png"; everything works as it should - smooth & clean (except the photo always stays the same which is not good).
But when the attribute src of img is changed all functions that should slowly fadeOut content don't seem to be working. The picture shows without any fade out effect. What is interesting, not only the image is affected as other sections also stop working, they just appear (although they worked fine before adding the line).
I fell like this is a bit more complex so if anyone could help I would be soooo grateful.
If I should add anything to help you understand the code please let me know.
Solved it!
If anyone experiences the same problem try to lower the resolution of photos, in my case they were too big to enable the website work flowlessly.

JavaScript not resizing height of UL element sometimes when inserting LI elements using Jquery

I have an Html/JavaScript application that contains N columns that need to be large enough contain all of the possible LI elements from all of the columns.
The simple solution seems to count the heights of all of the items in each column, compensate for padding, and then set the height to that total for each of the columns.
This works great when the LI elements contain plain text. Unfortunately, when the LI elements contain images instead, various browsers have problems. For example, when I first load the page in FireFox, it looks like the screenshot below, but upon another refresh, it works fine. It doesn't work as expected in Chrome either.
My application does not pre-populate the LI elements when the page loads - it uses JavaScript, as follows:
function populateUnsetAnswers(unsetCategoryAnswers) {
for (i in unsetCategoryAnswers) {
if (unsetCategoryAnswers.hasOwnProperty(i.toString())) {
$('#categoryQuestionArea #possibleAnswers').append(
categoryAnswerLiTag(unsetCategoryAnswers[i])
);
}
}
}
function categoryAnswerLiTag(unsetCategoryAnswer) {
var html = '<li id="' + unsetCategoryAnswer.id + '">';
if (unsetCategoryAnswer.image) {
html += '<img class="categoryAnswerImage" title="';
html += unsetCategoryAnswer.text;
html += '" src="/trainingdividend/rest/streaming/';
html += unsetCategoryAnswer.image.fileName;
html += '" style="height: ';
html += unsetCategoryAnswer.image.height;
html += ';';
html += '" />';
} else {
html += unsetCategoryAnswer.text
}
html += '</li>';
return html;
}
When the page is done loading, an ajax request fetches all of the objects to be put into LI elements, and then calls the first function above.
After all of the LI elements are created, I call this function right after it:
function resize() {
var currentHeight, totalHeight;
totalHeight = 0;
$("#categoryQuestionArea ul").children().each(function() {
currentHeight = $(this).height();
totalHeight += currentHeight + 13;
});
$("#categoryQuestionArea ul").height(totalHeight);
$("#categoryQuestionArea div#separator").css("padding-top", (totalHeight / 2) + "px");
}
Is there any way to tell jQuery, "Don't call resize() until all of the LI's are fully loaded and the images have rendered" ?
I think what's happening is that on the initial page load, the height of these LI elements is 0 or a small value because it doesn't contain the image, so my resize function is calculating the wrong result (I tested this with some alert statements). As long as the LIs are populated and the images have loaded, the total height is calculated just fine.
Any help? Thanks
To literally answer the question you asked, if you want to only call resize() when all images have finished loading, then you need to install onload handlers for those images and when you've recorded that the last one is now loaded, you can call the resize() function. You could do that like this (code explanation below):
var remainingAnswerImages = 0;
function categoryAnswerImageLoadHandler() {
--remainingAnswerImages;
if (remainingAnswerImages === 0) {
resize();
}
}
function populateUnsetAnswers(unsetCategoryAnswers) {
// add one extra to the image count so we won't have any chance
// at getting to zero before loading all the images
++remainingAnswerImages;
var possibleAnswers$ = $('#categoryQuestionArea #possibleAnswers');
for (i in unsetCategoryAnswers) {
if (unsetCategoryAnswers.hasOwnProperty(i.toString())) {
possibleAnswers$.append(categoryAnswerLiTag(unsetCategoryAnswers[i]));
}
}
// remove the one extra
--remainingAnswerImages;
// if we hit zero on the count, then there either were no images
// or all of them loaded immediately from the cache
// if the count isn't zero here, then the
// categoryAnswerImageLoadHandler() function will detect when it does hit zero
if (remainingAnswerImages === 0) {
resize();
}
}
function categoryAnswerLiTag(unsetCategoryAnswer) {
var obj = document.createElement("li");
obj.id = unsetCategoryAnswer.id;
if (unsetCategoryAnswer.image) {
// count this image
++remainingAnswerImages;
var img = new Image();
img.onload = img.onerror = img.onabort = categoryAnswerImageLoadHandler;
img.title = unsetCategoryAnswer.text;
img.style.height = unsetCategoryAnswer.image.height;
img.src = "/trainingdividend/rest/streaming/" + unsetCategoryAnswer.image.fileName;
obj.appendChild(img);
} else {
obj.innerHTML = unsetCategoryAnswer.text;
}
return obj;
}
By way of explanation, this code makes the following changes:
Add a variable remainingAnswerImages to keep track of how many images still need to be loaded.
Add an onload handler for each <img> tag that is created so we can keep track of when it's loaded.
Each time we generate the HTML for an tag with the onload handler, increment remainingAnswerImages.
When you're done adding all the HTML, check the remainingAnswerImages count to see if it's zero (this would only be the case if there were no images or if all images loaded immediately from the browser cache). If so, call resize() immediately.
In the onload handler which will be called for each image, decrement remainingAnswerImages and if the count has reached zero, call resize().
While adding images, add one extra to remainingAnswerImages as a gate to keep from getting to a zero count until we're done adding images. When done adding images, take that one extra out.
I also rewrote the categoryAnswerLiTag() function to just create the DOM objects directly rather than concat a bunch of strings together into HTML. In this case, the code is a lot cleaner to read and maintain.
I also moved the $('#categoryQuestionArea #possibleAnswers') out of your for loop since it resolves to the same thing every time. Better to do it once before the loop. Also, in most cases, this could be simplified to $('#possibleAnswers') since ids are supposed to be unique in the page.
Here is a jquery plugin that checks the images have loaded: https://github.com/alexanderdickson/waitForImages
Sample usage for your case would be:
$('#categoryQuestionArea').waitForImages(function() {
resize();
});
I would also just check for the total height of the <ul> instead of looping through the list items as you would have to manually change the script if either the padding, margins, or borders on the list items changes later on.
If you do have problems with images on the first page load maybe it is due to the fact that they are not cached and therefore not immediately available. So measuring their height will lead to bad results... did you debug the height that was fetched via jQuery (for example
currentHeight = $(this).height();
console.log(currentHeight);
The only way to do that is I think to observe the load events of all images (and probably the error as well) and count whether all request have been finished
try using
$('img').load(function(){
//put code here
});
I guess your HTML is screwed up. Particularly, your <img> tags.
Add the width and height attributes to your <img> tags. Everything will be magically solved.
See this jsfiddle to understand what I mean: http://jsfiddle.net/Ralt/Vwg7P/
Even though there is no image in there, the width and height attributes will occupy the space required for the image. As soon as the DOM is loaded.
This is rather a CSS problem, most probably due a fixed height, with items either floated or absolutely positioned.
There are number of ways to fix this.
Give a min-height instead of fixing a height.
#container { min-height: 100px; }
Clear the float and do not set any heights
#container { overflow: hidden; }
Use Scripts to add up to the height, once every element is added. Like the jQuery snippet below
$("#container").append($("#theimg"));
$("#container").height($("#container").height()+$("#theimg").height());
I think I might have a solution for you.
The main idea of my solution lies in CSS. You want to have 3 columns of the same height, right? You can have something like this: http://jsfiddle.net/agilius/NvzZp/46/
There is quite a lot of CSS there, but the main idea is this:
I simulate a 3 column layout under the actual content, with the .inner and .column classes.
The content is placed over (via z-index 2 > .inner zindex 1), with the same width as the columns that are under.
When content is added to the content zones, the height of the main #container updates.
Since .inner is top,left,right,bottom = 0, it updates, and since the .columns have 100% height, they update their height to match the #containers height.
Observations.
You can have padding, borders, margins in the .column class as you see fit.
No javascript is required.
Another simple Equal Height CSS solution:
LOGIC is very simple -
all of the columns/LI are floated
with .eH{ padding-bottom: X; margin-bottom: -X } and
wrapper/UL is .eW{overflow: hidden}
X= large arbitrary amount of px for factor of safety
EXAMPLE:
http://jsfiddle.net/rahen/TXVYD/4/
This sounds exactly like one of the problems i had when coding SudoSlider.
Below i've copied the code i solved it with. Just call autoheightwidth(i, 0, true) inside your resize() function.
The basic idea is that you do not know when the browser has completed loading the images, so instead of relying on a single height adjustment, you adjust the height every time something happens (mostly just that an image has been loaded).
It should work if you change the references of "obj" and "li" in the first 2 methods.
It's not very readable, but there was a big focus on size when i coded it.
// Automaticly adjust the height and width, i love this function.
// Before i had one function for adjusting height, and one for width.
function autoheightwidth(i, speed, axis) // Axis: true == height, false == width.
{
obj.ready(function() {// Not using .load(), because that only triggers when something is loaded.
adjustHeightWidth (i, speed, axis);
// Then i run it again after the images has been loaded. (If any)
// I know everything should be loaded, but just in case.
runOnImagesLoaded (li.eq(i), falsev, function(){
adjustHeightWidth (i, speed, axis);
});
});
};
function adjustHeightWidth (i, speed, axis)
{
var i = getRealPos(i); // I assume that the continuous clones, and the original element is the same height. So i allways adjust acording to the original element.
var target = li.eq(i);
// First i run it. In case there are no images to be loaded.
var b = target[axis ? "height" : "width"]();
obj.animate(
axis ? {height : b} : {width : b},
{
queue:falsev,
duration:speed,
easing:option[8]/*ease*/
}
);
}
function runOnImagesLoaded (target, allSlides, callback) // This function have to be rock stable, cause i use it ALL the time!
{
var elems = target.add(target.find('img')).filter('img');
var len = elems.length;
if (!len)
{
callback();
// No need to do anything else.
return this;
}
function loadFunction(that)
{
$(that).unbind('load').unbind('error');
// Webkit/Chrome (not sure) fix.
if (that.naturalHeight && !that.clientHeight)
{
$(that).height(that.naturalHeight).width(that.naturalWidth);
}
if (allSlides)
{
len--;
if (len == 0)
{
callback();
}
}
else
{
callback();
}
}
elems.each(function(){
var that = this;
$(that).load(function () {
loadFunction(that);
}).error(function () {
loadFunction(that);
});
/*
* Start ugly working IE fix.
*/
if (that.readyState == "complete")
{
$(that).trigger("load");
}
else if (that.readyState)
{
// Sometimes IE doesn't fire the readystatechange, even though the readystate has been changed to complete. AARRGHH!! I HATE IE, I HATE IT, I HATE IE!
that.src = that.src; // Do not ask me why this works, ask the IE team!
}
/*
* End ugly working IE fix.
*/
else if (that.complete)
{
$(that).trigger("load");
}
else if (that.complete === undefined)
{
var src = that.src;
// webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f
// data uri bypasses webkit log warning (thx doug jones)
that.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=="; // This is about the smallest image you can make.
that.src = src;
}
});
}
I think the Browser does not know the images' dimensions, because they are not loaded.
Either try to wrap the invocation of resize in a
jQuery(document).load( function funcName() {
...
} )
or give the image width and height attributes in the HTML img tags.
Maybe both

What is going on behind the scenes and is this the proper way to do this? (modifying the DOM via Javascript)

Not knowing the proper way, after much research on the web I found so many different ways to do something its confusing. The way I tried, and kinda worked is the following...
My CSS
#Content {
left:0px;
top:1px;
width:988px;
z-index:1;
background-color: #FFFFFE;
}
My JS
function Gradients(id) //<- this id not used during testing, i hard coded it below
{
var getit = document.getElementById("Content");
getit.style.backgroundColor="#CCCCCC";
//alert(origcolor);
//var value = document.getElementById("Content").style.backgroundColor;
//var value = document.styleSheets[0].cssRules[0].style.backgroundColor;
}
My HTML (just a test)
<div onClick="Gradients("Content");">Gradients Test:<span>#XXXXXX</span></div>
Firebug Results - bad?
<div id="Content" style="background-color: rgb(204, 204, 204);">
WHAT I'M TRYING TO ACCOMPLISH
My goal was to read the background of an input field (each has an id) and slowly change it to red FROM the DEFAULT color in the CSS to let them know the field was incorrect.
Right now my website just slams it to red and I thought - how hard can it be to gradient a color. So, my mainpage has less clutter so I thought I would try to gradient the background of something. As with all web stuff it's messier than I thought.
I even spent a couple of hours reading up on jQuery but I don't want to pull in a whole library for this 1 tiny thing I will be doing.
Other Info
It's kinda like how THIS stackoverflow website fades from yellow to white the DIV of my question when I come here. Except mine will be in input fields. I have some commented out stuff in my JS because I was trying different things. I removed some of the things that were ugly. It works as is BUT I don't know if it's a good way to do it because firebug shows it added something to the DIV inline.
I like clean code... and my code up there seems ugly because I added something to the DIV. Can't I change the CSS value or is this the proper way to do it?
A couple of questions...
1) Proper way to do it?
2) If thats the proper way to do it how do I delete that change and have it revert back to the CSS style? Or an ugly method would be to just stick the original value I stored before performing the gradient.
3) YOUR much better clean way of doing it :)
4) Is there an elegant way to READ the value in the CSS style sheet?
The reason I didn't go with the document.stylesheets is to me....it seemed ugly... what if it's not [0]. How do I know it will always be [0]. What if it's different in different browsers? sigh. I don't fully understand the DOM. I understand what child nodes and parent nodes are but when looking through firebug it's a huge mess all over the place and I have no clue where to find things, how to insert things and I don't like modifying the DOM much anyways - i would love a simple thing like this (and yes, I am guessing on the code below - if only it could be that easy) lol...
I wish it was this easy in javascript...
$original_color = getElementById("Content").style.backgroundColor;
// loop through starting AT the original_color and gradient to red somehow
//start loop here
getElementById("Content").style.backgroundColor = newcolor;
// end loop here
Awaiting an infusion of wisdom please :)
WHAT I TRIED RECENTLY AFTER POSTING and Reading examples on here -- My JS
var RGradient = 0;
var GGradient = 0;
var GStop = 0;
var BGradient = 0;
var BStop = 100;
var idGradient;
function Gradients(id)
{
var startcolor = "#FFFFFE";
RGradient = hexToR(startcolor);
GGradient = hexToG(startcolor);
BGradient = hexToB(startcolor);
idGradient = document.getElementById(id);
window.setTimeout("GradientIt()", 10);
}
function GradientIt()
{
if (GGradient == GStop && BGradient == BStop) return;
if (GGradient > GStop) GGradient--;
if (BGradient > BStop) BGradient--;
idGradient.style.backgroundColor="#"+(RGradient).toString(16)+(GGradient).toString(16)+(BGradient).toString(16);
document.getElementById('gtest').innerHTML = "#"+(RGradient).toString(16)+(GGradient).toString(16)+(BGradient).toString(16);
window.setTimeout("GradientIt()", 5);
}
function hexToR(h) { return parseInt((cutHex(h)).substring(0,2),16) }
function hexToG(h) { return parseInt((cutHex(h)).substring(2,4),16) }
function hexToB(h) { return parseInt((cutHex(h)).substring(4,6),16) }
function cutHex(h) { return (h.charAt(0)=="#") ? h.substring(1,7) : h}
ERROR in IE
I'm getting an error in IE AFTER it turns the background to red... - Invalid Property in Line 29 which is the line with all the toString(16)'s in it above.
Can someone explain why it's giving an error in IE please? I am checking if I'm above 0 so the numbers should stay 0 or higher. The other browsers don't give an error that I can see. Once it's working I will be changing it - this is just a "hacked together" test - I'll make it more efficient later on when it's on the page I want.
I spent about an hour trying to pass variables to setTimeout before I realized I can't. UGH! lol. Globals :( Can't wait for CSS3 full compatibility in ALL browsers.
I would suggest achieving this using either css3 or jquery (a javascript library)
To do it with css3 is rather simple, this article should have all the necessary information
http://net.tutsplus.com/tutorials/html-css-techniques/css-fundametals-css-3-transitions/
To do it with jQuery you will need to download jquery and preferably have a little bit of experience with javascript although it is not generally required to pick up jQuery for simple things like this. This is the jQuery function you would want to use:
http://api.jquery.com/animate/
#content {
left:0px;
top:1px;
width:988px;
z-index:1;
background-color: #FFFFFE;
transition: 0.3s;
-moz-transition: 0.3s;
-webkit-transition: 0.3s;
}
#content:focus {
background-color: #f00;
transition: 0.3s;
-moz-transition: 0.3s;
-webkit-transition: 0.3s;
}
The above is CSS3 and works in many browsers. However IE support is (as always) lacking.
via javascript/jquery....
function animate_bg(ele, from, to) {
from += from > to ? -1 : 1;
if(!$.support.opacity){
if(from != to){
var opStr = (Math.round(from * 25.5)).toString(16);
//alert(opStr)
ele.css({background:'transparent',filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#" + opStr + "fffff0, endColorstr=#" + opStr + "f00000)"});
}else{
ele.css({background:'transparent',filter:"none"});
}
}else{
ele.css("backgroundColor", "rgba(255, 0, 0, " + (from) / 10 + ")");
}
if(from != to)
setTimeout(function() { animate_bg(ele, from, to) }, 60);
}
and usage....
animate_bg($('...'), 8, 0);

Complete function before starting next (fadeIn/fadeOut)

UPDATED (see notes at bottom)
I have created an image map and when you hover over a specific section of this image map a description will appear in a designated area (the sidebar) of my website.
Each description is of varying length therefore I have not set any maximum height level for my sidebar area so that the display can grow vertically to accomodate each description.
The problem I am having is that when you rapidly hover over areas of the image map the display produces some weird results; showing blocks up content from another hot spot for a split second in full beneath the newly hovered over area and corresponding description (hope that makes sense)
Is there anyway to complete one function in full before displaying the next to avoid this nasty display/animation?
Here is my code:
$(document).ready(function() {
$("#a-hover").hide();
$("#a").hover(function() {
$("#a-hover").fadeIn();
}).mouseleave(function() {
$("#a-hover").fadeOut();
});
$("#b-hover").hide();
$("#b").hover(function() {
$("#b-hover").fadeIn();
}).mouseleave(function() {
$("#b-hover").fadeOut();
});
$("#c-hover").hide();
$("#c").hover(function() {
$("#c-hover").fadeIn();
}).mouseleave(function() {
$("#c-hover").fadeOut();
});
And my CSS;
#a-hover,#b-hover,#c-hover {
z-index: 2;
float: left;
position: relative;
}
#a-hover,#b-hover,#c-hover,{
position: relative;
top: 0px;
left: 0px;
z-index: 1;
width:326px;
min-height:603px;
background-color:#dedddd;
}
I have shortened my code for readability (I have 9 image map hot spots)
I am a novice when it comes to jQuery but I am making a committment to learn so please go easy as my code may not be up to scratch!
I have tried to solve this myself before posting here, but I am out of my depth and need some expert advice
I appreciate any responses.
Thank You,
Wp.
UPDTAE: I tried the majority of what was provided here as answers and whilst I believe these answers are on the right track I couldn't get the problem to stop however I did notice improvement in the animations overall.
I ended up using a combination .stop(true,true); and **resize font automatically.
**Ultimately not getting the desired result is due to my lack of polish with jQuery but being in a rush I managed to find another way to handle this issue (auto resizable font).****
Thanks to all who took the time out to answer and for those reading this for a similar solution at least know the .stop(true,true); properties did in fact work for me to solve one part of this problem.
Try adding .stop before each fadeIn and fadeOut. You should pass true, true to stop to complete the animating instantly rather than leave it half faded in:
$("#a").hover(function() {
$("#a-hover").stop(true, true).fadeIn();
}).mouseleave(function() {
$("#a-hover").stop(true, true).fadeOut();
});
You can also get rid of all of the repetition by binding on a class instead of id's:
$(".imageMapElement").hover(function() {
$("#" + $(this).attr("id") + "-hover").stop(true, true).fadeIn();
}).mouseleave(function() {
$("#" + $(this).attr("id") + "-hover").stop(true, true).fadeOut();
});
May be you can try Jquery Hover Intent plugin.
try stopping the other functions:
$("#a").hover(function() {
$("#b-hover").stop().hide();
$("#c-hover").stop().hide();
$("#a-hover").fadeIn();
}).mouseleave(function() {
$("#a-hover").fadeOut();
});
Try adding .stop() before each .fadeIn and .fadeOut -- that will cancel any previous animations and immediately begin your new one.
You also have a problem with using .hover() -- that actually encapsulates two actions, mouseover and mouseout. When you assign two functions to it, the first is mouseover and the second is mouseout, but when you assign only one function to it, that one function is used for both mouseover and mouseout. So, in effect, your code is causing the element to fadeIn and fadeOut on mouseout.
Incidentally, you can shorten your code a lot using standard jQuery techniques:
$("#a-hover,#b-hover,#c-hover").hide().hover(function() {
$(this).stop().fadeIn();
}, function() {
$(this).stop().fadeOut();
});
...or even better yet, assign a class to each of those three IDs and select it instead.
You have to chain all the jQuery function calls!

jQuery .css("margin-top", value) not updating in IE 8 (Standards mode)

I'm building an auto-follow div that is bound to the $(window).scroll() event. Here is my JavaScript.
var alert_top = 0;
var alert_margin_top = 0;
$(function() {
alert_top = $("#ActionBox").offset().top;
alert_margin_top = parseInt($("#ActionBox").css("margin-top"));
$(window).scroll(function () {
var scroll_top = $(window).scrollTop();
if(scroll_top > alert_top) {
$("#ActionBox").css("margin-top", ((scroll_top-alert_top)+(alert_margin_top*2))+"px");
console.log("Setting margin-top to "+$("#ActionBox").css("margin-top"));
} else {
$("#ActionBox").css("margin-top", alert_margin_top+"px");
};
});
});
This code assumes that there is this CSS rule in place
#ActionBox {
margin-top: 15px;
}
And it takes an element with the id "ActionBox" (in this case a div). The div is positioned in a left aligned menu that runs down the side, so it's starting offset is approximately 200 px). The goal is to start adding to the margin-top value once the user has scrolled past the point where the div might start to disappear off the top of the browser viewport (yes I know setting it to position: fixed would do the same thing, but then it would obscure the content below the ActionBox but still in the menu).
Now the console.log shows that the event is firing every time it should and it's setting the correct value. But in some pages of my web app the div isn't redrawn. This is especially odd because in other pages (in IE) the code works as expected (and it works every time in FF, Opera and WebKit). All pages evaluate (0 errors and 0 warnings according to the W3C validator and the FireFox HTMLTidy Validator), and no JS errors are thrown (according to the IE Developer Toolbar and Firebug). One other part to this mystery, if I unselect the #ActionBox margin-top rule in the HTML Style explorer in the IE Developer Tools then the div jumps immediately back in the newly adjusted place that it should have if the scroll event had triggered a redraw. Also if I force IE8 into Quirks Mode or compatibility mode then the even triggers an update.
One More thing, it works as expected in IE7 and IE 6 (thanks to the wonderful IETester for that)
I'm having a problem with your script in Firefox. When I scroll down, the script continues to add a margin to the page and I never reach the bottom of the page. This occurs because the ActionBox is still part of the page elements. I posted a demo here.
One solution would be to add a position: fixed to the CSS definition, but I see this won't work for you
Another solution would be to position the ActionBox absolutely (to the document body) and adjust the top.
Updated the code to fit with the solution found for others to benefit.
UPDATED:
CSS
#ActionBox {
position: relative;
float: right;
}
Script
var alert_top = 0;
var alert_margin_top = 0;
$(function() {
alert_top = $("#ActionBox").offset().top;
alert_margin_top = parseInt($("#ActionBox").css("margin-top"),10);
$(window).scroll(function () {
var scroll_top = $(window).scrollTop();
if (scroll_top > alert_top) {
$("#ActionBox").css("margin-top", ((scroll_top-alert_top)+(alert_margin_top*2)) + "px");
console.log("Setting margin-top to " + $("#ActionBox").css("margin-top"));
} else {
$("#ActionBox").css("margin-top", alert_margin_top+"px");
};
});
});
Also it is important to add a base (10 in this case) to your parseInt(), e.g.
parseInt($("#ActionBox").css("top"),10);
Try marginTop in place of margin-top, eg:
$("#ActionBox").css("marginTop", foo);
I found the answer!
I want to acknowledge the hard work of everyone in trying to find a better way to solve this problem, unfortunately because of a series of larger constraints I am unable to select them as the "answer" (I am voting them up because you deserve points for contributing).
The specific problem I was facing was a JavaScript onScoll event that was firing but a subsequent CSS update that wasn't causing IE8 (in standards mode) to redraw. Even stranger was the fact that in some pages it was redrawing while in others (with no obvious similarity) it wasn't. The solution in the end was to add the following CSS
#ActionBox {
position: relative;
float: right;
}
Here is an updated pastbin showing this (I added some more style to show how I am implementing this code). The IE "edit code" then "view output" bug fudgey talked about still occurs (but it seems to be a event binding issue unique to pastbin (and similar services)
I don't know why adding "float: right" allows IE8 to complete a redraw on an event that was already firing, but for some reason it does.
The correct format for IE8 is:
$("#ActionBox").css({ 'margin-top': '10px' });
with this work.
try this method
$("your id or class name").css({ 'margin-top': '18px' });

Categories

Resources