Monday, August 9, 2010

Replacing Smart Quotes

I hate smart quotes. If you are looking for a way to get rid of all of the special Windows characters in your input that can screw things up, here it is in three lines of PHP:

$pattern = array(chr(128), chr(132), chr(133), chr(145), chr(146), chr(147), chr(148), chr(149), chr(150), chr(151), chr(153), chr(162), chr(166), chr(169), chr(174), chr(194), chr(226)); $replacements = array("", "", "...", "'", "'", '"', '"', "•", '–', '—', "'", "™", "...", "©", "®", "", ""); $input = str_replace($pattern, $replacements, $input);

PHP's xml_parse will choke and die if it hits any of the above characters. It won't throw a warning or anything as far as I can tell, it'll just stop processing the input as if it had received an EOF. Sanitize your input from smart quotes!

Monday, July 12, 2010

PHP posing as XML

Want to set up your own custom RSS feeds in PHP? Need to pass a dynamic XML document to another service? It's easier than you think! Disguise your PHP file as XML with the following headers:

header("Pragma: public"); header("Content-Type: application/xml; charset=utf-8");

The output of your PHP document should be valid XML. The headers will convince whatever's reading it that it's an XML file.

Your file can still use the .php extension (and will need to, unless you set up some special server-side handling), but you can refer to it as if it were an XML document. It works in RSS readers, etc. just as if it were an XML file, but you can generate the content dynamically and change it depending on the GET parameters and so forth.

Presumably, you can use these headers in a language that isn't PHP to achieve the same effect.

Thursday, July 8, 2010

Drop Shadows in CSS with jQuery

We were discussing drop shadows around the office today. CSS natively supports it, it turns out – as long as you're running Safari or Opera. For the rest of us, we can get around it with a little trickery.

The method I'm using doesn't require Javascript, but Javascript automates the process, meaning you don't have to duplicate everything by hand.

<script type="text/javascript"><!-- $(function() { $('.shadowed').each(function (index, elem) { $(elem).append('<div class="drop-shadow" style="opacity: 0.25;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=25);position:absolute;top:2px;left:2px;">' + $(elem).text() + '</div>'); $(elem).parent().height($(elem).parent().height() + 4); $(elem).children('.drop-shadow').css('font', $(elem).css('font')); $(elem).children('.drop-shadow').css('color', $(elem).css('color')); $(elem).children('.drop-shadow').width($(elem).width()); $(elem).css('position', 'relative'); }); }); --> </script>

Then, all you have to do is declare an element with the "shadowed" class and let Javascript take care of the rest. The drop shadow will automatically assume the correct position and style.

For simplicity's sake, the CSS for the drop-shadow class is declared in the script. You may want to move it into your CSS file to de-clutter things.

Caveat: If the parent element to your shadowed element is subject to resizing, you will need to update the drop-shadow element's width when the parent width changes. Also, you will want the parent element to have some padding to keep the drop shadow from overrunning it.

Here's an example of the drop shadow in action. (If you view the source, you'll notice that I hard-coded the CSS for this example.)
Here's an example of the drop shadow in action.

Tuesday, July 6, 2010

Animating images with Javascript

Warning: This is a really stupid web design trick. You are almost always better off using an animated GIF instead of relying on the browser to animate your image for you. The only excuse that makes this even marginally acceptable is that you might need the partial alpha transparency PNGs afford.

That said, it's really easy to create animations with Javascript's setInterval command:

<script type="text/javascript"><!-- setInterval(function() {animate('myElement');}, 1000); // change frames every 1000 ms function animate(id) { var elem = document.getElementById(id); var img = elem.style.backgroundImage; if (img == null) return; if (img.indexOf('images/image2.png') != -1) { elem.style.backgroundImage = 'url(images/image1.png)'; } else { elem.style.backgroundImage = 'url(images/image2.png)'; } } --></script>

The preceding example will animate the background image of the element with the ID "myElem."

Again, I don't recommend using this without a good reason. So here it is; take it with a grain of salt.

Thursday, July 1, 2010

Converting a text field to a password

A project I'm working on called for the login fields to display the words "EMAIL ADDRESS" and "PASSWORD" in gray, until the user clicked on them, at which point the fields were to behave normally. Here's the script I used -- this is vanilla Javascript, no jQuery.

function convertField(field, isPassword) { field.style.color = '#000000'; field.style.backgroundColor = '#FFFFFF'; field.onfocus = null; field.value = ''; if (isPassword) { var field2 = field.cloneNode(false); field2.type = 'password'; field2.onfocus = null; field.parentNode.replaceChild(field2, field); setTimeout(function () { field2.focus(); }, 5); } }

For your HTML, you want something like this:

<input type="text" value="EMAIL ADDRESS" onfocus="convertField(this, false)"> <input type="text" value="PASSWORD" onfocus="convertField(this, true)">

The password field is text-type; that's not a mistake. It will become a password field when focused, but until then, we want the contents to be displayed in plain text.

An example in action (with a little extra styling):

Wednesday, June 30, 2010

Styling file input fields

File input fields are a pain for CSS-reliant designers. They can't be styled the way most everything else can. On top of that, the "Browse" button is actually part of the form element. If you're trying to give your form a different look, this can be a real pain.

There's a way around it, and while it's not exactly clean, it lets you use the style you want and it retains the form's functionality across browsers. The basic idea is to hide the actual input element behind a fake, nicer-looking one.

First, the CSS:

div.filerow { width: 250px; /* Feel free to change the dimensions, */ height: 40px; /* but you'll want to make them fixed */ position: relative; background-image: url('button_browse.png'); background-repeat: no-repeat; background-position: right center; display: block; overflow: hidden; } div.fakefile { /* This should be styled like your form */ width: 165px; /* You may need to change this */ height: 15px; padding: 1px; position: absolute; left: 0px; overflow: hidden; } input.file { position: relative; height: 100%; width: 230px; top: -4px; opacity: 0; -moz-opacity: 0; filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0); z-index: 2; }

Some explanation: We actually need the field to be above the fake field (so that clicking in that area focuses it), so we give it a higher z-index but make it transparent. The file field doesn't work with percentage widths (surprise!), so you'll need to manually set the width to be a little smaller than the actual field. (The width style is completely ignored by Firefox, which we'll address below.) Height works as a percentage in IE and FF – go figure – and we want it to be as big as possible. When working with the dimensions, it can be helpful to make the field opaque so that you can see how it lines up.

The "Browse" button becomes a background image. You may need to adjust its position, depending on what image you're using. Note that IE doesn't automatically activate the field when you click on the text area, so using a Browse image is highly recommended.

Onto the HTML:

<div class="filerow fileinputs"> <div class="fakefile" id="fakefile"></div> <input type="file" class="file" size="50" name="myFile" tabindex="32767" onchange="document.getElementById('fakeformElement').innerHTML = this.value" onfocus="this.blur()"> </div>

A few things to note: first, we always blur the field. This prevents the focus from being set on a field the user can't see. Second, we set the tabindex arbitrarily high to allow the user to navigate the form normally with tab. Finally, we stuff the value of the real field into the fake one to make it look like it's real.

Finally, as mentioned, CSS width is ignored on the file element, so we use size instead. Don't worry about getting this right, since IE ignores it and FF doesn't care where on the field you click. An arbitrarily high value will work. This is why we set overflow:hidden.

Et voila, you have a styled file input field. Overly complicated? Yes. But until CSS lets you style the file field (and for a good while after, if you care about compatibility), this is what you've got.

Here's a live example:

Upload a file:

Tuesday, June 29, 2010

jQuery crossfade

I spent a significant chunk of my time today looking for a simple way to crossfade one element into another on hover. Nothing I found was a great fit, and the one I eventually decided to try degraded until I realized I wasn't actually using any of the code I'd picked up. Here's what I ended up with:

$(document).ready(function() { $('.crossfade').css('position', 'relative'); $('.crossfade').children().css({position: 'absolute', top:0, left:0}).each(function(i) { $(this).css('z-index', i); }); $('.crossfade img:last-child').hide(); $('.crossfade').hover(function() {$(this).children('img:first-child').fadeOut(500); $(this).children('img:last-child').fadeIn(250);}, function() {$(this).children('img:first-child').fadeIn(250); $(this).children('img:last-child').fadeOut(500);}); });
The HTML is extra-simple:
<div class="crossfade"> <img src="image1.png" /> <!-- this is the regular image --> <img src="image2.png" /> <!-- this image shows up on hover --> </div>

That's it -- the only other thing you need to do is make sure your div is the right size. Very easy to plug into your page. Note that the fadeIn is faster than the fadeOut; this makes the transition smoother.

This script can easily be adapted to use other triggers or to work with non-image elements.

And an example – hover to try it out: