Wednesday, May 18, 2011

The Base Tag

Whenever someone asks me to help troubleshoot a problem with their site, if I can't immediately see the problem or fix it using Firebug, I download the page as an HTML only file. Then you have to deal with pages that have relative links to code, style sheets or images:
<link href="/css/main.css" rel="stylesheet">
which you will probably have to use your editor to find and replace each one. A big time saver is using the apparently less known base tag, in which you add the base URL to the site.
<base href="http://some-site.com">
Then if you need to modify a file for testing, just download it to your computer (e.g. your desktop), open it in your browser, then copy the URL. Replace the file's url with this one, since the relative URL will look to the base tag instead of your desktop:
<link href="file:///C:/Desktop/main.css">
Now when you open the page in your browser, it should look exactly like the online page.  :)

Sunday, May 15, 2011

jQuery find/replace text without destroying DOM elements

I found this question on StackOverflow on how to replace text without destroying event handlers, DOM nodes and such. He just wanted to replace a name inside a block of HTML. The name could be inside of an LI, A (link), p or div element, etc. Here is the example HTML markup provided:
<div id="test">
    <h1>An article about John</h1>
    <p>The first paragraph is about John.</p>
    <p>The second paragraph contains a <a href="#">link to John's CV</a>.</p>
    <div class="comments">
        <h2>Comments to John's article</h2>
        <ul>
            <li>Some user asks John a question.</li>
            <li>John responds.</li>
        </ul>
    </div>
</div>
You couldn't simply grab the html() and replace the text because it the result replaces all of the elements inside of "#test" and thus breaks all previously attached functions (click, hover, etc.) or any entered form data inside of inputs (demo):
$('#test').html(function(i, v) {
    return v.replace(/John/g, 'Peter');    
});
Instead you'd have to step through each text node, find the name, then replace it - it's quite a chunk of code. But I think I found a quick and dirty solution (demo) that works on the HTML markup above:
$('#test :not(:has(*))').text(function(i, v) {
  return v.replace(/John/g, 'Peter');    
});
What this does is find all elements that don't have children, and replace the text inside.

To understand what the selector is doing, first imagine the selector like this: "#test *:not(:has(*))". So reading it out in plain language would sound like this: Find the element with an ID of test, then find all elements within it (the first asterisk "*"), then find the opposite ":not()" of all elements that have children ":has(*)", meaning elements that don't have children. Then get the text and replace it.

But this quick and dirty method does have an important limitation which is that it would ignore text inside an element that has child elements. Here is an example of what I mean where the link is treated as a child element and thus the name is not replaced (nothing would change):
<div id="test">
    <p>Visit John's <a href="blog.html">blog</a></p>
</div>
So, if you do have markup like as it is above, then just wrap the name in a span (demo):
<div id="test">
    <p>Visit <span>John's</span> <a href="blog.html">blog</a></p>
</div>
If you don't or can't wrap the name, then you can't use the quick and dirty method I posted above. You'll have to use this textWalk plugin by PatrickDW (a demo, and see his answer for full details) or check out Bobince's answer in this question for a similar method of walking through the text nodes.

Monday, May 2, 2011

FancySelector

I just created FancySelector which replaces the standard select with a more visually appealing select. It allows you to scroll through the options with either the keyboard or mouse. Selecting multiple options is similar but not exactly the same as using a standard select. Please see the documentation for more details.

There are two demos, a full screen demo (pictured below) and an inline selector demo.


This jQuery plugin is available for download on github.