Gotcha with getting offsets in Javascript
September 18th, 2008
Over the past several days I’ve been playing around with jQuery, trying to retool a couple things I’ve been working on. This is my first foray into jQueryland and it’s been pleasant thus far. I did, however, run into a confusing little issue with pageX and pageY.
What’s your vector, Victor?
The problem I was running into happened to be using e.pageY and it’s ilk. In my function they were returning the wrong coordinates. At least that’s what I thought. After much searching and patience — and a little frustration — I believe the problem is solved.
At first I didn’t realize just how far off the positioned element was when just using e.pageY and that’s why it was frustrating. After trying other options like offsetY, clientY, and el.offsetTop amongst others, I was no closer to a solution. I finally measured just how far off the positioned element was and low and behold, it matched the offset of the main parent div (in this case #container).
So, now I had to get the #container’s offset — #container is a set width with margin: 0 auto;. Once I had the offset of #container I could subtract that from e.pageY.
Just how did I get #container’s offset? Well, there are two ways I found, after searching, to figure it out.
var topOffset = document.getElementById('container')[0].offsetY— meaning, we need to find the element in the DOM and by setting and index (best to go with 0) we could use Javascript’soffsetYand/oroffsetX.var containerPosition = $('container').offset(); var containerTop = containerPosition.top;— meaning, using jQuery and it’s nativeoffset()function, we grab the top and left offset of the element in the DOM.
That’s all she wrote
In the aftermath, I would say pageY and pageX were returning the proper coordinates based on the containers they were in. Unfortunately the coordinates weren’t taking the container offset into account. Whether this is a problem with how my Javascript was written I don’t know. At least I know how to solve it.
Sliding backgrounds with -webkit CSS
September 10th, 2008
After days — and several attempts — of scratching my head and trying to figure out how to have sliding backgrounds, ala Dave Shea’s A List Apart article with CSS only, I think I’ve cracked it. Here’s a quick look at how you can set up sliding CSS backgrounds using -webkit CSS properties.
Quick caveat about compatibility
This CSS will only work in WebKit nightlies or, at the time of writing, Safari 4 Developer Preview.
Some of the current browsers may have some aspects of the CSS used in this example but not all. Hopefully we’ll see more and more browsers picking up some of these properties and behaviours.
On with the example
Probably the best way to present this example is by using a list — in our case a navigation list — but I’m sure a creative person could make it work in other situations. The jist of the process is to have 3 layers of elements and a creative use of positioning them.
Setting up the containers
The parent container (ul) should have the background state for the list (how about a nice blue background: -webkit-gradient(linear, left top, left bottom, from(rgba(137,174,195,1)), to(rgba(147,185,207,1)), color-stop(0.25,rgba(140,177,199,1)));). Using relative positioning and overflow: hidden; height: 1.5em; we can then position the list items (li) out of view (position: relative; top: 2em;). Unfortunately this also pushes the links inside the lis out of view so we need to position them back into view with position: relative; top: -2em;. So now we’ve got a navigation list with a blue background.
Now we need to add a background to the li that is currently out of view (let’s go with neutral grey gradient background: -webkit-gradient(linear, left top, left bottom, from(rgba(200,200,200,1)), to(rgba(50,50,50,1)), color-stop(0.95,rgba(100,100,100,1)));). This is what will slide into view as the visitor hovers over the link.
Get sliding already
Finally, we need to add the CSS that’s going to do the sliding. All of the example code I’ve given so far, with the exception of -webkit-gradient — which can be replaced with background colours or images — will work across all the browsers out today. The follow bits are where we dip into strictly WebKit territory.
First we’ll need to slide the li back into view with top: 0; -webkit-transition: top 0.3s ease-in-out;. It’s a little spotty though and I noticed some odd flickering when testing the code this far. So, I made sure the link stays where it is in the list by adding the same CSS from the li.
Bob’s your uncle
And that’s that. We’ve now got a navigation list, where when a link is hovered a new background slides in from the bottom. Obviously numerous parts of this example can be changed — sliding direction, colours, heights, container/elements, timing, etc.
Check out the working example (only works in WebKit nightlies and Safari 4 Developer Preview as of this writing) I threw together for you.
Here’s the complete HTML I used in my example:
<!DOCTYPE html> <html lang="en-us"> <head> <meta charset="utf-8" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>CSS Sliding Background Images</title> <link rel="stylesheet" href="sliding-bg.css" type="text/css" media="screen" charset="utf-8" /> </head> <body> <div id="container"> <div id="main"> <ul id="navigation" class="horizontal"> <li id="home"><a href="#">Home</a></li> <li id="projects" class="active"><a href="#">Projects</a></li> <li id="blog"><a href="/mike/">Blog</a></li> <li id="twitter"><a href="http://twitter.com/mstickel">Twitter</a></li> </ul> </div> <!-- /main --> </div> <!-- /container --> </body> </html>
And the full CSS:
/* General page stuff */
html { background: #e5e5e5; min-height: 100%; }
body { background: #fff; font: 12px/18px "MyriadPro-Regular", "Lucida Grande", "Lucida Sans Unicode", Helvetica, sans-serif; margin: 20px auto; width: 900px; }
/* utility classes */
ul.horizontal { float: left; list-style: none inside; position: relative; width: 100%; *height: 1%; }
ul.horizontal li { float: left; margin: 0; }
/* Navigation specific */
ul#navigation {
background: -webkit-gradient(linear, left top, left bottom, from(rgba(137,174,195,1)), to(rgba(147,185,207,1)), color-stop(0.25,rgba(140,177,199,1)));
border-left: 1px solid #fff;
margin: 0;
overflow: hidden;
min-height: 1em;
padding: 0;
position: relative;
width: 400px;
}
ul#navigation li {
background: -webkit-gradient(linear, left top, left bottom, from(rgba(200,200,200,1)), to(rgba(50,50,50,1)), color-stop(0.95,rgba(100,100,100,1)));
border-right: 1px solid #fff; width: 24.75%;
position: relative;
top: 2em;
}
ul#navigation a {
border-right: 1px solid #fff; width: 24.75%;
color: #000;
display: block;
height: 1em;
padding: 0.4em 0 0.7em;
text-align: center;
text-decoration: none;
width: 100%;
position: relative;
top: -2em;
}
ul#navigation li:hover {
top: 0;
-webkit-transition: top 0.3s ease-in-out;
}
ul#navigation a:hover {
top: 0;
-webkit-transition: top 0.3s ease-in-out;
color: #fff;
}
ul#navigation li.active a { background: url(bg.png) no-repeat bottom left; }
ul#navigation li.active a:hover { color: #000; }
More -webkit-transition funniness.
September 9th, 2008
Today I was revisiting my last attempt at doing sliding backgrounds or transitions with WebKit specific CSS properties. For some reason I thought there was another way around it. So far, I haven’t found one to achieve the effect I’m going for but I did find something very interesting about -webkit-transitions.
The What
In order to use -webkit-transition on a -webkit-gradient in the background property, it seems you need to make sure there is some transparency in the -webkit-gradient. It doesn’t seem to matter which part of the gradient has transparency — the to(), from(), or color-stop() — just as long as one of them has it.
Let me explain
In my last example I discovered that it was not possible to transition from one background using -webkit-gradient to another. That still holds true.
However, what I didn’t realize at the time is that, for some reason, I couldn’t get transitions to work on background gradients that were defined with hexadecimal colours. I tried the following combinations:
- From:
background: -webkit-gradient(linear, left top, left bottom, to(#f00), from(#00f), color-stop(0.5, #f00));
To:background-color: #c80; - From:
background: -webkit-gradient(linear, left top, left bottom, to(rgba(0,0,255,1)), from(rgba(255,0,0,1)), color-stop(0.5,rgba(50,50,255,1)));
To:background-color: #c80;and - From:
background: -webkit-gradient(linear, left top, left bottom, to(rgba(0,0,255,1)), from(rgba(255,0,0,1)), color-stop(0.5,rgba(50,50,255,.1)));
To:background-color: #c80;
Guess which one worked?
The Finale
This has shown me two things.
- In order to transition a background colour with
-webkit-transitionand-webkit-gradientyou need at least one property of the-webkit-gradientto be somewhat transparent — though it does seem to work best when the transparency is oncolor-stop. - The
-webkit-gradientsits on top of thebackground-colorandbackground-imageproperties — meaning that it has a higher z-index.
Now go, my web minions, and play with whatever gradients you can come up with — it’s what makes websites part of Web 2.0 don’t you know. ;)
FancyZoom. But edgier.
September 5th, 2008
The talk around town is that a couple boys, in some podunk town, wrote some Javascript code that has given them more hits in the last couple days then they could have imagined. Not sure it’s all that fancy though, it still uses tables and that just won’t do.
Even with my busy schedule, I took it upon myself to help these fellows out. There’s no reason that we should be using tables for layout anymore. IE6 is a piece of crap. We all know this. Firefox 3 is getting better — or is the best in some regards, depending who you talk to — and Safari/Webkit is pretty kick ass. So why don’t we pander to the better browsers and give the rest whatever they can handle?
Enter FancyZoom, but edgier. Ok, so the tagline/name is pretty lame but whatever, I’m not a copywriter.
What’s the difference?
The biggest difference is that it uses -border-radius and box-shadow, in the browsers that support them. No more transparent PNGs and tables. The code’s slightly leaner with two divs instead of a whole table.
Don’t fret young padawans, the old way is still there for those that want to use it. In fact, the old way is the default until people start getting a little bolder in their effort to move forward. In order to see the new hotness, you’ll need to add edgy:true as an option to the first new FancyZoom call.
For example:
Prototype:
$$("div.photo a").each(function(el) { new FancyZoom(el, {edgy:true}); }) or new FancyZoom('medium_box_link', {width:300, height:200, edgy:true});.
jQuery:
$('div.photo a').fancyZoom({scaleImage:true, closeOnClick:true, edgy:true}); or $('#medium_box_link').fancyZoom({edgy:true});
Caveats
Keep in mind, when using prototype, the edgy option has to be set in the first FancyZoom call, otherwise you’ll get the old, transparent PNG version. Also remember that once edgy is set, it’s set for all FancyZoom calls on that page.
Why? Because the code for the zoomed box is only inserted once into the HTML document. It’s not inserted on the fly or for each instance of the class. This was probably done by design but adding this edgy option might change that. We’ll see.
When using the jQuery version, edgy has to be set in each call.
Check out the example I ripped made for you.
You can’t transition a gradient, apparently.
August 30th, 2008
While I was mulling through my feeds tonight I saw a link to Dave Shea’s CSS Sprites2 article on A List Apart. To be honest, I haven’t actually read it — though I will soon, promise — but seeing the examples put a bug in my brain. Maybe those effects could be done with CSS3 transitions/animations.
So, I threw together a quick little navigation list and went to work putting some basic styles on it — nothing nearly as fancy as Dave did. What I wanted to do is use -webkit-gradient for a background color, then use -webkit-transition to fade or slide in a different color or background.
Unfortunately this version of the test failed. It would seem that there is no way to transition a -webkit-gradient using -webkit-transition. If I’m wrong, please let me know.
Here’s what I did find out:
background: -webkit-gradient(...);won’t transition to anotherbackground: -webkit-gradient(...);(using-webkit-transition: background 0.3s linear;)- You can transition an element from
-webkit-gradientto abackground-color(using-webkit-transition: background-color 0.3s linear;) - You cannot transition an element from
background-colortobackground: -webkit-gradient;(using either-webkit-transition: background 0.3s linear;or-webkit-transition: background-color 0.3s linear;) - Transitioning multiple properties — at least in this case —
background-colorandcolorthe long hand of-webkit-transitionneeds to be used (-webkit-transition-property: background-color, color; -webkit-transition-duration: 0.3s, 0.3s; -webkit-transition-timing-function: linear, linear;)
Check it out for yourself using this code:
<style type="text/css" media="screen">
ul#nav { float: left; list-style: none outside; width: 100%; }
ul#nav li { border-right: 1px solid; float: left; width: 24.75%; }
ul#nav li.last { border: none; }
ul#nav li a {
/* background-color: #08c; */
background: -webkit-gradient(linear, left top, left bottom, from(rgba(20,50,100,0.6)), to(rgba(40,70,150,0.6)), color-stop(0.5,rgba(30,60,125,0.6)));
color: #000;
display: block;
line-height: 2;
text-align: center;
width: 100%;
}
ul#nav li a:hover {
background-color: #c80;
/* background: -webkit-gradient(linear, left top, left bottom, from(rgba(40,70,150,1)), to(rgba(20,50,100,1)), color-stop(0.5,rgba(30,60,125,1))); */
color: #fff;
-webkit-transition-property: background-color, color;
-webkit-transition-duration: 0.7s, 0.3s;
-webkit-transition-timing-function: linear;
}
</style>
<ul id="nav">
<li id="home"><a href="#">Home</a></li>
<li id="projects"><a href="#">Projects</a></li>
<li id="blog"><a href="#">Blog</a></li>
<li id="misc" class="last"><a href="#">Misc</a></li>
</ul>
What it looks like (best viewed in the latest version of Webkit or Safari):