Povert

It's Pronounced "Pah-vert." You povert.

Archive for the ‘WWW’ Category

What’s Wrong With Ads

Friday, January 8th, 2010

Ads drive me nuts.

Not for the reasons it drive some people nuts — as long as they’re not intrusive, I don’t mind. I don’t regularly use AdBlock, though I do use ClickToFlash in Safari for various reasons. I don’t even think that Flash ads, per se, are a problem.

I see two basic problems:

  1. document.write()
  2. Invalid markup

document.write()

I hate document.write(). I’m pretty sure that I am That Guy At Work Who Hates document.write() With Unreasonable And Embarrassing Passionâ„¢, but it really does limit your options and it contributes to poor page load and rendering times.

Amazingly, a quick search of my past entries here turned up very little about my disdain for document.write(), so I’ll briefly summarize the problem.

Web browsers don’t handle external scripts very well. Because of things like document.write(), the browser actually has to pause the rendering of the page. Unfortunately, if the script is long-running or the server it’s on is slow or non-responsive, this can significantly increase the apparent page load time. Several mildly misbehaving external scripts on a page can have a dramatic cumulative effect. I set up an example of this while back to demonstrate this.

This is how most ads are delivered to a page. So a single ad placed anywhere before any important content on a page can block the display of that page until the ad is displayed.

There are ways around this. One way would be to have a external javascript file called early in the page. Then ad positions on the page wouldn’t be additional external script tags — they would be calls to a function or functions in that main javascript file (I believe Google Adsense allows for something like this method, at least in some circumstances). You could even get away with document.write() in this case, though that wouldn’t be eliminating problem #2. More on that in a moment. But there is still a basic problem here — we’re down to one external script, but if its host server is unresponsive, the display of all or most of the page will be held up until it resolves or times out.

Another way would be to dynamically create script tags and append them to the document’s head. These tags would point to the external scripts. But because they’re inserted this way, the browser doesn’t have to hold up the page rendering. YUI’s Get.script() does exactly this with some handy features thrown in as well. The big problem here is that document.write() is not compatible with this method. Unfortunately, 100% of ads out there are written out with document.write() by ad servers, regardless of whether they were written that way.

I’ve tried overriding document.write(). Last I checked, it actually works in Firefox:


(function(){
var els = document.getElementsByTagName('*'),
el = els[els.length - 1];
adURL = 'http://ad-server.com/blah';
document.write = function(str) {
if (typeof str != 'undefined') {
var newEl = document.createElement('span');
newEl.innerHTML = str;
var nodes = newEl.childNodes;
while (nodes.length) {
el.parentNode.appendChild(nodes[0]);
}
}
}
YAHOO.util.Get.script(adURL, {
onSuccess: function(){document.write = dwrite;}
});
})();

Guess how that pans out in IE?

(Yes, there are some flaws in the above code, but it was proof-of concept.)

And no, the defer attribute does not work when using document.write() and should be avoided anyway.

Invalid Markup

This problem is actually a consequence of problem #1. document.write() allows you to just toss arbitrary text and markup onto the page. There’s nothing to stop you from slapping this onto an otherwise nice page:


document.write('<div>Tacos</div>');

This can utterly destroy a page’s layout. All it takes is one bad ad to toss in one unmatched tag and a page’s layout will look like it got punched in the face and set on fire. The same thing could happen using innerHTML. However, using DOM methods avoids this problem:


var div = document.createElement('div');
adSpan.appendChild(div);
div.appendChild(document.createTextNode('Tacos'));

Yes, that appears to be more work. If your gripe with this solution is that you have to type more, please allow me to do to your face what a stray closing div can do to a page.

Mistakes can be made when doing it this way, but it eliminates an entire category of error that can sometimes be fairly difficult to track down. Even if you made 100% sure that your ad doesn’t output invalid html, all it takes is one person in the chain to do a bad copy & paste job to hose it up. It happens. It costs money and wastes time. It also makes me want to make the streets run red with the blood of my enemies.

Iframes, sadly, are not a solution to this problem. While it would contain the bad markup, they make certain type of ads hard or impossible to do. There is also a very long-standing Firefox bug in which iframes magically swap contents with each other.

What Can Be Done?

So where can we go from here? I wish I knew. I don’t think it’s realistic to expect that everyone writing ads out there change the way they write them. Maybe someone with Google’s clout could do this, but I can’t imagine that that would end well even for Google.

To make matters worse, whether the ad author avoids document.write() or not is actually irrelevant at this point because ad servers like DoubleClick actually take your ad code and document.write() it — leading to the silly (but common):


document.write('<noscript>Blah blah blah</noscript>');

Overriding document.write() would be a good intermediate step if it worked in IE, but it doesn’t.

This really is a serious problem. I can’t imagine how much potential revenue is lost to slow rendering pages as a result of ads which were supposed to generate revenue in the first place, not to mention the actual money spent on tracking down bad ads that are hosing up a page’s layout.

YUI Get.script Manager

Friday, August 21st, 2009

I threw this together for some extra-curricular activities at work. Figured someone may find it useful.

I put it under a BSD license, so feel free to do what you want with it.

yGetMan is a simple queue manager for YUI’s Get.script. If you request a script that’s already been loaded, it doesn’t attempt to re-load that script. Whether this is useful to you depends on your environment and circumstances.

See it in action here.

Here it is: yGetMan. Also, minified. Comments in there explain usage.

Also, if someone creates a mascot — a Get Man, if you will — I will make a big deal out of this and celebrate with a big plate of spaghetti.

CSS-centric JS

Thursday, September 11th, 2008

This probably isn’t news to anyone deep in JS/CSS land, but I thought these notes may be handy for someone who’s still figuring out the best way to approach some problems.

There’s a temptation, I think, to go overboard with JS when manipulating elements on a page. Many tasks can be more simply handled with a combination of JS and CSS.

For example, let’s say you have a simple unordered list:

<ul id="happyList">
  <li><a href="tacos.html">Tacos</a></li>
  <li><a href="burritos.html">Burritos</a></li>
  <li><a href="enchiladas.html">Enchiladas</a></li>
  <li><a href="tamales.html">Tamales</a></li>
  <li><a href="chalupas.html">Chalupas</a></li>
  <li><a href="antacids.html">Antacids</a></li>
</ul>

Let’s say that you only want 3 items to show by default, and you want a “More” link at the bottom that will expand the list to show the rest.

There’s a few ways you could approach this. People may be tempted to put an onclick event onto the “More” link, then have that call a function that toggles the visibility of each element after the third. Something like:


var happy = document.getElementById('happyList');
var items = happy.getElementsByTagName('li');
function showLinks() {
  for (var i=3; i < items; i++) {
    items[i].style.display = 'list-item';
  }
}
function hideLinks() {
  for (var i=3; i < items; i++) {
    items[i].style.display = 'none';
  }
}

That would work (though I haven’t actually tested this particular js), but it’s definitely the crappy way, for a number of reasons. First, there’s no need for two functions, though that’s beside the point of this post. Second, we’re running through a for() loop every time each function is called.

Instead of something like that, the way I’d do it is to first set up this CSS:


#happyList.collapsed li {
  display: none;
}
#happyList.collapsed li.alwaysShow {
  display: list-item;
}

Then, you put a class of ‘alwaysShow’ on the first three list item elements, either in the html itself or with javascript:


function assignShow() {
  var happy = document.getElementById('happyList');
  var items = happy.getElementsByTagName('li');
  for (var i=0; i < items.length && i < 3; i++) {
    items[i].className = 'alwaysShow';
  }
}

Now, when the ul has a class of ‘collapsed’, only the first three items show:


function toggleList() {
  var happy = document.getElementById('happyList');
  if (happy.className == 'collapsed') {
    happy.className = '';
  } else {
    happy.className = 'collapsed';
  }
}

Or, more succinctly:

function toggleList() {
  var happy = document.getElementById('happyList');
  happy.className = happy.className ==
    'collapsed' ? '' : 'collapsed';
}

Then, either in HTML or using javascript, you can add an element with the “More” text and attach an event to it that calls toggleList().

I’m being a little vague on details here. My main point is that it’s preferable to lean on CSS as much as possible. It helps to avoid micromanaging elements.

Dynamic Resizing of Cross-Domain Iframes

Monday, May 19th, 2008

If you’ve ever wanted to have an iframe resize itself based on its (cross-domain) contents, you may have run into some problems.

I had to tackle this somewhat recently. The solution is silly, but it works.

The problem is that an iframe can’t communicate with its parent if they’re on different domains. For example, let’s say I put an iframe here on povert.com. The iframe could be pointing to, say, flappingcrane.com. Povert.com can’t access any useful information about the iframe, and vise-versa.

The solution is to have the iframed content (in this case flappingcrane.com) iframe something else from the original domain. So you could set up a file on povert.com called yaygo.html. Then flappingcrane.com iframes that, with a GET query. That GET query should contain any information you want to communicate to the parent (povert.com). Since yaygo.html is on povert.com, it can refer to povert.com with parent (though, since they’re different domains, it can’t do anything useful) and to flappingcrane.com with parent.parent. Because they’re on the same domain (even with the intermediate flappingcrane.com iframe), it can do something useful — like pass values from the GET query to a function:


parent.parent.setFunProps(iframeid, width, height, tacoflavor);

setFunProps() can then take those values and do what it wants with it (resize the iframe, whatever).

Another tidbit. If you give a name attribute to an iframe, that iframe’s contents can access the name with window.name. You can’t access window.id though, unfortunately. Either are handy for identifying to the parent document what iframe is requesting a resize, though there are other crappier ways to do it (passing its own url to match against iframe src, for example).

UPDATE: As I mention in my comment below, you can see an example of this at http://povert.com/fun/iframe/.

UPDATE (Oct 28, 2009): Looks like it doesn’t work in any version of IE now. It’s possible that Microsoft saw this as a security hole and fixed it.

UPDATE (Oct 18, 2011): jajasilver pointed out that the examples didn’t set a DOCTYPE (oops — thanks!). This has been corrected and the examples work again. Wow, I hadn’t looked at this for years.

Povert is proudly powered by WordPress
Entries (RSS) and Comments (RSS).