Archive for Category ‘HTML‘

 
 

Resize a Crossdomain iFrame

When it comes to crossdomain quirks with frames and Ajax, there’s not usually a “good” solution — there’s just one that works. Something I was working on the other week had a “well it works” moment, although the solution was far from ideal.

The Problem

The page in question is the Sponsored These Tweeters page at SponsoredTweets. It’s a pretty basic Wordpress page with an iFrame that contains the list of Tweeters. Real easy way of adding a dynamic touch to a Wordpress site. The issue is that the iframe contains a dynamic amount of content, and could potentially grow or shrink based on the length of amount of tweeters being shown. The kicker? The page with the list of tweeters is at a subdomain, and under https.

Monitoring with Jquery

My first idea with anything like this is “Can I just listen for a jquery event on the element?”. Well, sure, but the height of the iframe won’t change. You’ll only be able to get the height/width of the iframe itself, not the content within it from the page itself, so watching for a resize or load event on the iframe won’t work. What you’re really wanting to listen for is a change in the height of the content

Works? No.

Reaching into the frame with contentWindow

If your frame and the page containing it both exist on the same domain, you can reach into DOM of the frame and get the height that way. At anytime you can do something like this to get the height of the page:

document.getElementById("iframeid")
         .contentWindow.document.body.scrollHeight

This bit of code will return an integer — the height of the content of the frame in pixels. Even if the iframe has a height of (for example) 300px, the content itself could be smaller or larger. Unfortunately you can’t set a jQuery event watcher on document.getElementById("iframeid").contentWindow.document.body either (at least from what I’ve seen), so I can’t see a way to watch for a change in height.

The “not-so-nice” fix for this is to make a really fast function that repeatedly checks a change in the height, and resizes the iframe accordingly. Here’s some sample code for this using jQuery.

var $iframe = $("#iframeid");
function resize_iframe() {
  var current_height = $iframe.css("height");
  if(current_height != $iframe[0].contentWindow.document.body.scrollHeight) {
    $iframe.css("height", $iframe[0].contentWindow.document.body.scrollHeight);
  }
}
setInterval("resize_iframe();", 100);

Everytime I use setTimeout() or setInterval() in Javascript, it’s immediately a red flag. These (along with the evil-eval) can be an obvious code smell, but also a trigger that the code itself isn’t designed in the best way.

The problem with this though is that it won’t quite be realtime. Also the contentWindow.document object is not available cross-domain. Even doing things like contentWindow.document.location.href to get the current location of the frame doesn’t work if the calling page and the frame are on different domains/subdomains. This might work for some cases, but not if your iframe is on a different domain.

Works? Yes.
Works on the same domain? Yes.
Works on different subdomains on the same domain? No.

The document.domain hack

If both your frame and your calling page exist on the same parent domain, there’s a little hack you can do to get this to work. Each page has a document.domain variable that contains the domain from which ajax calls can be made to. If you rails application is up at https://app.sponsoredtweets.com, then your document domain will be set to app.sponsoredtweets.com. Of course your main website sitting at http://sponsoredtweets.com will have a document.domain ot sponsoredtweets.com. Because of this they won’t be able to talk to each other. What you can do is manually change the document.domain value.

Surprisingly enough, you can tweak this value as long as you only get broader in your domain. For instance, if document.domain is app.sponsoredtweets.com, then you’ll be able to manually set this using something like this: document.domain = "sponsoredtweets.com" From that point on, that page will be treated as those requests were coming/going to that domain. Using this, you could set your iframe and the page containing the iframe to be on the same root domain, then reach into it’s contentHeight as described above. After you’ve set this to “sponsoredtweets.com” though, you won’t be able to change it again, as that’s the most general a document.domain can get.

Works on the same domain? Yep.
Works on different subdomains on the same domain? Yes.
Works crossdomain? No.
Works across https? No.

Frame within a Frame within a Frame

Unfortunately, none of this works across domain, of from http connections to https. Here’s how it works for SponsoredTweets:

The main page is http://sponsoredtweets.com/tweeters/sponsor-these-tweeters/, aka, the parent page.
Which has a frame to https://app.sponsoredtweets.com/tweeters, aka, the content page.
Which has a frame to http://sponsoredtweets.com/iframe.html, aka, the placeholder page.

The middle frame is the one that will change in height of course. For the same reasons as above, the middle frame cannot reach into it’s parent frame and call methods there either. It can, however, control it’s own iframe in a very limited sense. For instance, take this line from the

$("#parent_domain")[0].contentWindow.location = 'http://sponsoredtweets.com/iframe.html#' + height;

Whenever the height of the content page changes, it updates the location of it’s frame to include a hash tag followed by the current height of the content. By itself this won’t do anything, but if that iframe.html page is watching for changes to it’s location, we can start from there.

The placeholder page is what makes this all possible. All it has to do is monitor for changes to it’s hash, and when it sees them, send them up to the parent page (that is, the top level page). Now, although the content page can’t talk to it’s parent page, the placeholder page can talk to it since they are both on the same domain. Is it a hack? Oh yeah, and of the worst kind.

<html>
<script type="text/javascript">
<!--
  function sendHash() {
    var location = window.location.href.split("#")
    if(location.length != 2) { return; }
    parent.parent.receiveHash(location[1]);
  }
  setInterval("sendHash();", 100);
-->
</script>
<body></body>
</html>

parent.parent…? Yeah, thanks HTML. All the receiveHash() function on the parent page does is take a number and set it as the height of it’s iframe. Real simple implementation, it’s just the craziness that is cross-domain security that makes it difficult to grasp.

Works across https? Yes!

Got a Better Solution?

I can’t believe this would be the ideal way to do something as simple as resize an iframe. Do you know of a better way of doing this given that the pages exist on separate domains? I’d love to hear about it.

Handcrafted CSS Book Review

Handcrafted CSS As a developer now a days, it’s becoming more and more important to have a firm grasp on front-end development. The rise of Ajax has impacted more than just Javascript though — the document object model has been a driving factor in how easy it is to work with an Ajax heavy page. Getting a handle on good DOM design and the use of the right HTML elements with CSS markup does more than just make the HTML look pretty — it makes your life MUCH easier when it comes to maintenance and Javascript integration.

A few years ago, I read an amazing book that turned me on to this idea called Bulletproof Web Design by Dan Cederholm. You can read my glowing review as a sign that it’s a book I often recommend. Handcrafted CSS is a the latest book by Cederholm, and runs a similar track. While BulletProof is geared towards designers with some CSS experience, with the goal of refining their skills, Handcrafted CSS focuses on how to integrate CSS3/HTML 5 techniques into your existing skill set.

Browsers Matter

One of the main questions the book asks is Do Websites Need to Look Exactly the Same in Every Browser? (actually, that site is featured in the book). While previous design ideas concentrated on creating a look that was identical in every browser, Cederholm focuses a lot of attention on the idea of progressive enhancement. Instead of crafting every little detail the same in multiple browsers, certain details could be considered browser specific. Rounded corners are a prime example. They are an absolute pain to do cross browser, yet if you concentrate only on Firefox/Webkit/Opera you can round corners in a single line, or at most a few lines:

.rounded {
  border: 1px solid #3792b3;
  border-radius: 4px;
  -webkit-border-radius: 4px;
  -moz-border-radius: 4px;
}

One thing you’ll notice a lot throughout the book, is a focus on cross-browser support for CSS3. For instance, the border-radius attribute is not yet implemented by Firefox of Safari – the implement browser specific rules instead. So although the border-radius property won’t be used by these right now, once these browsers support the final version of CSS3, you won’t have to make any changes to your CSS. This futureproofing, and paying attention to flexibility is what Cederholm considers as craftsmanship; where the developer goes a step beyond the result and concentrates on longevity.

What’s this RGBA thing?

Although I’d heard about just about every concept in this book, it put a new spin on all of them. There was one addition, however, that I was completely unaware of — RGBA. Defining colors using RGB has been possible for a good decade or so, but the A makes all the difference. RGBA (red, green, blue, alpha) defines the amount of red/green/blue (as integers from 0 to 255), as well as a transparency amount from 0-1. Much of what you can do with RGBA was available using the opacity CSS attribute, but some of the major differences are highlighted.

h1 { color: rgb(55, 146, 179); }
h1 em { color: rgba(55, 146, 179, .8); font-size:80%; }

The first rule uses only rgb, generating a 100% opacity, normal font color. The inner <em> uses rgba for colors, specifying a .8 alpha amount. This creates a very pleasant affect where the emphasis text is a little bit lighter than the h1 text. As someone who get annoyed picking out tons of very similar font colors, being able to just lower the alpha transparency to create new colors is a huge time saver. It offers an additional bonus over setting opacity as well. Take for example a popup box on a page that you want to have a partial opacity — like a lightbox. If you set the opacity of this box to something, say .25, then the box and the text within the box will both be partially transparent. Usually you’ll just want the box itself to be like this. Using RGBA you can do exactly that — setting the background of the box to a a .25 alpha amount.

Going a step Farther

These are some of the easiest to describe parts of the books, but it goes into many more ideas. Float management, fluid grid design and even adding the extra touch with some Javascript (jQuery is the language of choice) are all entire chapters. I grabbed the video version of the book as well, which I’m gad I did. It features just over an hour of Dan Cederholm talking about each of the 10 chapters from the book. For the most part it’s all him talking — not reading from the book — but elaborating on some of the ideas. It occasionally shifts to the demo Tugboat side used in the book, with voice over description. If you read through the book completely you won’t learn anything new from it, but it serves as a good reminder of what the major focuses of the book are distilled to 65 minutes.

Who is this book for?

If you already have your head around CSS, and want to elevate your skills with some CSS3 spice, and focus on craftsmanship for current CSS ideals, this is a good one to checkout. What it’s not is an encyclopedia of all that is CSS3. Not all chapters are on CSS3 either, although many of them mention solutions with IE6 incompatible of solutions. For these it usually gives an IE6 solution, using css hacks (rather than optional html includes for a special stylesheet). Even though it’s not comprehensive, what it does cover, it handles very well. My only complain would be that it seemed a bit short. I finished it on a plane ride — and I’m not a fast reader. The full code from the book is available on the official site, but only if you have the book. :) The Tugboat example used is very pretty though, and a lot can be learned from the source. If you see it a store, be sure to give it a once over and see if it’s for you.

Adding a Lifestream to Wordpress

Sometimes a new service comes around that you know is powerful but you have trouble finding an actual use for it. Two of those that I’ve recently been looking into are FriendFeed and Yahoo Pipes. By themselves they’re nothing, but once you have a goal in mind both become powerful allies.

Adding some kind of a “lifestream” seems like all the rage these days, so I decided to give it a try using FriendFeed. I have to admit they make this extremely easy. Creating a page via a single js call is about as easy as it gets, although if the page was going to be hit often it’d be a good idea to do some kind of caching. FriendFeed is nice enough to provide some css hooks into the generated HTML that help with the finishing touches.

There is one type of service that wasn’t available on FriendFeed that I wanted to add to it though — movie reviews. Most movie sites are still walled gardens, despite the outcry from the open web community. IMDB has absolutely no API, although they do provide their data in txt files. That won’t help for getting your specific info though. FriedFeed does integrate with NetFlix, but it looks to be only for receiving queue notifications. Flixster seemed like the best choice for movie reviews, despite the awful interface of their website. Luckily you can do just about everything from Facebook. The problem is, Flixster doesn’t have any sort of API! Actually, no movie sites I’ve found provide so much as an RSS feed of your reviews (if you know of any, let me know). Enter Yahoo Pipes. Just a few weeks ago, someone created a Yahoo Pipe for transforming the XML that Flixster uses for it’s site widget into an RSS feed. Quite effective, and looks good (example). I still have my problems with Flixster, but at least it’s a start in integrating movie reviews.

The end result is a very basic lifestream page. There’s a lot more than can be done with this though. What if you were able to jump to a specific date and see activity? Or if there were check boxes to show/hide specific services? Not to mention all the styling you can do to it. Interested to see what people end up doing with their FriendFeed data.