UrlMasher beta published!

Check out my newly minted url mashing tool. UrlMasher is built to create links for multi-domain sites that share a common code base.

Usage:

  1. Enter your url domain, subdomain and/or path.
  2. Mash them up!
  3. Control-click the links.
  4. Enjoy your new tabs!

Under the hood:

To do:

  • Refactor localStorage mechanisms (not very DRY).
  • Part “delete” hotspot on mobile too small.
  • Add some exciting help copy.

Please post suggestions, criticisms and bugs in the comments (I’ll be moving UrlMasher to GitHub soon enough).

blockUI tip for removing inline styles

Quick tip! While using blockUI, I found the need to zap some CSS styles that were being added, inline, by the plugin. It took me a few minutes, but the answer was pretty simple (though not explicitly mentioned in the documentation). Just add “null” as the style’s value, like this:

$('#element').block({
    message: "You shall not pass!",
    css: {
        backgroundColor: null,
        border: null
    }
});

For even more customization, simply add a custom class through chaining, like this:

$('#element').block({
    message: "You shall not pass!"
}).find('.blockMsg').addClass('notification');

Safari iO S select dropdown bug fix

I came across an irritating iOS select dropdown bug while testing a production site on an iPad. The bug occurs when the user tries to select the first option where no other options have been selected previously, either via user interaction or by default.

Unfortunately, the simplest fix, to place an empty, disabled option in the first position, is blocked by another bug. In iOS, disabled options are not disabled. See this articlefor more information.

Trying to fix this issue was like weaving a new subway tunnel under an old city (slight exaggeration!). The fix I came up with is to prepend an empty option into all selects that did not have an empty option in the first position, and had no options selected by default.

I’m only executing the fix on iOS devices using a simple if statement.

if(navigator.userAgent.match(/iP(ad|hone|od)/i)){

$('select').each(function(){
    // set variables
    var $this = $(this), // cache this
        si = $this.attr('selectedIndex'), // find all selects that are not selected
        $options = $this.find('option'), // make object of options
        $firstOption = $options.filter(":first"); // filter first option

    // disable any existing empty first options
    if($firstOption.html() == ""){
        $firstOption.attr('disabled', 'disabled');
    }
    // all selects that do not have a "selected" attribute
    if (si === -1){
        //add temporary style to see if/where it is working
        //$this.css('border','2px solid green');
        // prepend an empty, disabled option if none exist
        if($firstOption.html() != ""){
            $this.prepend('<option value="" disabled="disabled"></option>');
            // set size +1
            $this.attr('size', function(i, val){
                return ++val;
            });
        }
    }
});

}

Textarea copy injector

I needed to build a UI component that injected some copy into a textarea. It needed to save any text that was already entered, and it needed to place the cursor at the end of the newly injected text (actually, it needed to select the last three characters). This is what I came up with:

$('#textareaInjectContent').bind('click', function (event) {
    var $eventTarget = $(event.target);

    if ($eventTarget.hasClass('text-inject')) {
        var $textarea = $("#textInjectee"),
            txtMessage = $textarea.val(),
            theCopy = $eventTarget.next().html(),
            newMessage = "";

        // trim spaces
        var txtMessage = txtMessage.replace(/^\s+|\s+$/g, '');

        // replace any non-breaking spaces
        var theCopy = theCopy.replace(' ', ' ');

        if (txtMessage === "") {
            newMessage = theCopy;
        }
        else {
            newMessage = theCopy + "\r\n\r\n" + txtMessage;
        }

        $textarea.val(newMessage);

        var textareaID = $textarea.attr('id');

        //Set position of cursor
        var textareaEl = document.getElementById(textareaID);
        if (textareaEl.setSelectionRange) {
            textareaEl.setSelectionRange((theCopy.length - 3), theCopy.length);
            //textareaEl.setSelectionRange(theCopy.length, theCopy.length);
        }
        else {
            e = textareaEl.createTextRange();
            e.collapse(true);
            e.moveEnd('character', theCopy.length);
            e.moveStart('character', theCopy.length - 3);
            e.select();
        }
        //Set focus on textarea
        document.getElementById(textareaID).focus();
        //$('#textCont').dialog('close');
    }
});

Here is the html structure:

<textarea id="textInjectee"></textarea>

<div id="textareaInjectContent">
<ul>
    <li>
        <button type="button" class="text-inject">Insert</button>
        <span class="text-to-inject">Turpis nonummy malesuada diam...</span>
    </li>
    <li>
        <button type="button" class="text-inject">Insert</button>
        <span class="text-to-inject">Lorem in suspendisse imperdiet fusce tortor purus et massa sit cras dignissim suscipit nec leo velit sapien et odio wisi lobortis ultrices lorem adipiscing...</span>
    </li>
    <li>
        <button type="button" class="text-inject">Insert</button>
        <span class="text-to-inject">Lobortis odio vel quisque ultricies in elit auctor...</span>
    </li>
</ul>
</div>

Here is a working jsfiddle: http://jsfiddle.net/McWatt/r99sC/embedded/result/

Quick email validation with jQuery

I whipped together this quick email validation script the other day. As far as validation scripts go, it is more basic than basic.

Here is the jQuery:

$('#inputID').bind('blur focus', function(event){
    if(event.type === 'blur'){
        //cache jquery objects
        var $invalidEmailError = $('#invalidEmailError'),
            $submitButton = $('#submitButton'),
            $this = $(this);

        var v = $this.val();

        //trim spaces
        v =  v.replace(/^\s+|\s+$/g, "");

        //check email against regex
        if(v.match(/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i)){
            $invalidEmailError.hide();
            $submitButton.removeAttr('disabled').removeClass('disabled');
            $this.addClass('email-good').removeClass('email-bad');
        }
        else{
            $invalidEmailError.show();
            $submitButton.attr('disabled','disabled').addClass('disabled');
            $this.addClass('email-bad').removeClass('email-good');
        }
        //replace email with trimmed version
        $this.val(v);
    }
    //remove status styles while editing
    if(event.type === 'focus'){
        $(this).removeClass('email-bad email-good');
    }
});

…and the html:

<input id="inputID" type="text" placeholder="Please enter an email" />

<button id="submitButton" disabled="disabled" type="submit">
    Send
</button>

<div id="invalidEmailError" style="display:none">
    Please enter a valid email address
</div>

…and some base css:

input[type="text"]{
    border:1px solid grey;
}
input[type="text"].email-bad{
    border:1px solid red;
}
input[type="text"].email-good{
    border:1px solid green;
}

There are some things to consider with the way the events behave. The email gets validated against the regex when the input blur event fires. This means that the user must either tab to the submit button or click the submit twice. A fix would be to validate on click of the submit, then submit if it passes validation.

If you are using Modernizr, I would recommend wrapping this validator in a conditional statement so any browser that supports html5 form validation gets the native functionality (be sure to include the appropriate attributes on the input). Perhaps this is something for a future post.

Demo: http://jsfiddle.net/McWatt/hNB8s/

Uses the following:

Simple tooltip (using title attribute on link)

Here is a very simple tooltip script I wrote a while back. There are tons of tooltip scripts out there, and most are much more complex than mine. I’ve decided to post it anyway, as perhaps it will come in handy for someone.

This simple tooltip script uses event targeting and pulls the tip from the title attribute of any element. The tooltip content is stored using the jQuery’s data api. This script works great for creating styled tooltips. It keeps the markup clean as it doesn’t add any new elements. It is not, however, the proper choice if any html is needed in the tooltip. For more robust tooltips, there are plenty of plugins available in the jQuery plugin repository.

Demo: http://jsfiddle.net/McWatt/PAUXv/

First, add the class “tooltip” and a “title” attribute with a value that contains your tooltip.

<a href="http://www.erikphipps.com" class="tooltip" title="Go to my home page!">Go to my homepage!</a>

Add the following javascript to a jQuery document ready function.

$('body').delegate('.tooltip', 'mouseover mouseout', function(event) {
    //cache the event and title content
    var $target = $(event.target),
        tipTitle = $target.attr('title');

    //if the title is not empty, fill the data with the title's content
    if (tipTitle != '') {
        $target.data('tipTitleData', tipTitle);
    }

    //empty title attribute so browser tooltip doesn't appear
    $target.attr('title', '');

    //build and show the tooltip on mouseover
    if (event.type == 'mouseover') {

        //stuff the tooltip html into a variable
        var tipHtml = '<div class="tooltip-content"><p>' + $target.data('tipTitleData') + '</p></div>';

        //append the tooltip html to body and position
        $(tipHtml).appendTo('body').css({ left: event.pageX + 'px', top: (event.pageY + 12) + 'px' }).show();
    }

    //remove the tooltip on mouseout
    if (event.type == 'mouseout') {
        $('.tooltip-content').remove();
    }
});

The only thing left, is to make it add some styling to make it presentable.

.tooltip{
    cursor:pointer;
}
.tooltip:hover{
    text-decoration:underline;
}
.tooltip-content{
    position:absolute;
    width:300px;
    box-shadow:0 0 8px #888888;
    border-radius: 2px;
    background-color:White;
    border:1px solid #333;
    padding:12px;
    display:none;
    line-height:1.5;
}