Posts Tagged ‘jQuery’

A better toggle button, with jQuery and CSS3

One of my more popular articles on this blog has been my jQuery toggle button article. However, my thinking has changed a lot since writing that post and, approaching the problem now, I’d likely do things differently. There were a few key flaws in my approach:

  • Using an additional image for the border. border-radius was still new at the time, but even so I think it would have been better to develop for the bleeding edge, and then deal with fallback for older tech.
  • Using jQuery animate. While I like Javascript as a control mechanism for animations, Javascript-based animations are another matter entirely. CSS3 transitions and transformations are a much better solution, they’re easier to handle and performance is, almost always, much better. Again, this was because I was avoiding the bleeding edge and focusing too much on backward compatibility, as transition and transform were still very new at the time.
  • Not making it a jQuery plugin. There was so much of a dependency on jQuery for selection, modifying the DOM, etc. that wrapping everything in a jQuery plugin made sense. My thinking was always that by not being bound too closely to jQuery, you can always just rip-out the jQuery bits and have a slim, pure JS component. However, there are 2 problems with this idea:
    • it’s not always that easy to rip-out jQuery, and doing so usually means re-creating much of the abstraction and utility functions offered by jQuery
    • plugin or not, you can still end up coupling heavily with jQuery
  • Widgetizing the HTML/CSS. The flaw here is writing out HTML and inline CSS from a Javascript string to create the button, which is usually not a good idea. It’s usually better to leave markup and styling in the document, where they can be readily manipulated or re-style. This is in line with my rant about Enyo (and widget frameworks in general), where I mentioned that widgets lead to a significant loss of flexibility. There is a case to be made for a toggle button widget, as it enables you to automatically replace checkboxes with toggle buttons without modifying any HTML but, in the general, I don’t think it’s worth it.

Below is an updated toggle button accompanied by the code that creates it. As in my previous post, the button itself is a single image, applied as a background-image to an anchor tag, and state transitions are done by shifting the background-position of the element.

Live Demo

HTML

<!-- toggle button markup -->
<a id="btn-toggle" class="btn-toggle" href="#">
    <
input name="yesno" type="checkbox" checked="checked" />
</
a>

CSS

/* toggle button style, styled for initial state (off) */
a.btn-toggle { margin:0; padding:0; display:block; border-radius:32px; background:url(base.png) -57px 0px no-repeat transparent; width:98px; height:64px; }

/* toggle button active state (on) */
a.btn-toggle.active { background-position:-7px 0px; border-color:#40A1EC; }

/* hide the underlying input checkbox of the toggle button */
a.btn-toggle input { display:none; }

Javascript

// jQuery plugin for toggle button
(function( $ ){

    $.fn.makeToggleButton =
function() {
        
    
this.each(function() {
            
            
var elem = $(this);
                    
            
// get the state of the underlying input checkbox
            
elem.val = function() {
                
return elem.find('input').is(':checked');
            }
                    
            
// function to toggle button state
            
elem.toggle = function() {
                    
                
var chkbx = elem.find('input');                    
                
if (!chkbx.is(':checked')) { // not check, switch on
                        
                    
elem.addClass('active');
                    chkbx.attr(
'checked', true);
                }
                
else {

                    elem.removeClass(
'active');
                    chkbx.attr(
'checked', false);
                }
            }
                    
            
// click handler
            
elem.click(function(e) {                    
                elem.toggle();
                e.preventDefault();                    
            });
                    
            
// adjust state to initial value of input checkbox
            
if(elem.find('input').is(':checked'))
            {
                elem.addClass(
'active');
            }
                    
            
// setTimeout to prevent transition upon setting initial state to active
            
setTimeout(function() {
                elem.css(
'transition', 'background-position 0.4s');
                elem.css(
'-webkit-transition', 'background-position 0.4s');
                elem.css(
'-moz-transition', 'background-position 0.4s');
                elem.css(
'-o-transition', 'background-position 0.4s');
                elem.css(
'-ms-transition', 'background-position 0.4s');
            }, 50 );
                
        });


    
return this;
            
    };
})( jQuery );        


// make toggle button when document is ready
$(document).ready(function() {
    $(
'#btn-toggle').makeToggleButton();
});

 

Elegant <img> presentation

While it’s easy to decry Flash and espouse the merits of HTML5, it’s worth taking a look at some of the positive elements found on many Flash sites and seeing what can be brought over to the HTML side. One very obvious aspect is the presentation of images. Flash sites almost never render an image in the way that an <img> tag is rendered by the browser. Flash sites will (typically) elegantly animate or fade an image in/out whereas the default browser behavior for HTML is to progressively render the image as it is received (sometimes images are 2D interlaced for a slightly better effect). Even worse, many HTML sites leave width and height as attributes to be determined dynamically for the <img> tag, resulting in page content shifting around as images are loaded.

Better image presentation is certainly possible with HTML + Javascript, and there are tons of beautiful JS image galleries on the web, but outside of such galleries most developers don’t attempt anything beyond the typical <img> presentation.

So, I did a little experiment with 2 goals in mind:

  • Elegant presentation of images with HTML + JS
  • Very simple markup, one iota above what’s necessary for a typical <img> tag

I’m pretty happy with the results.

elegant img loading

View the demo
(disable your browser cache so that loading latency is accurately taken into account)

Here’s what I did:

1. Make the <img> elements.

e.g.

<img
data-src="http://aautar.digital-radiation.com/elegant-img-loading/p1.jpg"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAAQSURBVHjaYvj//z8DQIABAAj8Av7bok0WAAAAAElFTkSuQmCC"
width="200"
height="150"
alt="photo" />

Things to note:

  • The data-src attribute is an HTML 5 custom data attribute. It specifies the URL of the image to be loaded.
  • The src attribute uses the data URI scheme to load and set the initial image displayed in the img element. Leaving the src attribute blank is not desirable as it will collapse the img element and only show the alt tag (the same behavior occurs initially if a URL to an image is specified). Specifying a data URI allows the image to be loaded as part of the document itself. The data URI shown above is for a 1×1, transparent PNG. A GIF preloader would probably be a good idea here.
  • The width and height attributes are specified. This is not strictly necessary, but in keeping with the theme of elegance, this prevent other content on the page from shifting after images are loaded.

2. Write the Javascript code

<script src="jquery-1.6.2.min.js" type="text/javascript"></script>
<
script type="text/javascript">

window.onload = function ()
{
$(
'img').each(function ()
{
var imgElem = $(this);
var src = imgElem.attr('data-src');

var img = new Image();
img.src = src;
img.onload =
function ()
{

imgElem.fadeTo(
'fast', 0.0001, function ()
{
imgElem.attr(
'src', src);
imgElem.fadeTo(
'slow', 1)
});

}

});
}

</script>

For simplicity, jQuery is used.

After the page is loaded, the data-src attribute of every <img> element is read. The image, specified by the data-src attribute, is loaded via a Javascript Image object and, once loaded (Image.onload event fires):

  • The <img> element is faded out (actually faded to an alpha of 0.0001 because a total fadeOut to 0 will effectively remove the element from the document rendering).
  • The src attribute is changed to the the URL of the loaded image.
  • The <img> element is faded in.

The important bit is changing the src attribute; the effect itself, whether it’s fading, sliding, etc. is not terribly important.

Reflex Feedback 0.3

A small update to the Reflex Feedback Widget,

  • Design tweaks (border, box-shadow, border-radius on textarea; padding-top on copyright)
  • widgetPos argument added to init(), valid values => ‘left’, ‘right’

reflex feedback widget 0.3

So you can now position the widget on the right edge of the page like this:

Reflex.init($('body'), 'controller/post_feedback.php', 'right');

More info about the widget itself can be found in the original blog post.

Better Checkboxes

Another take on styling checkboxes with jQuery+CSS by Martin Angelov.

Better Check Boxes with jQuery and CSS by Martin Angelov

I’m not sure if they should really be called checkboxes at this point. I did something very similar a while ago, I called them toggle buttons.

Reflex Feedback 0.2

An updated version of the Reflex Feedback widget is now up.

Changes:

  • New icons done by myself and released under the same license as the code; no longer using the ones by Yusuke Kamiyamane. This should also make it easier for those who want to do custom modifications as they don’t have to worry about an attribution requirement for the icons.
  • New menu layout and new buttons. Now using jQuery UI buttons for everything, which allows for compatibility with jQuery UI styling, ThemeRoller, etc.
  • CSS fixes to prevent some inherited styles from screwing up layout.

reflex feedback widget

For info on how to use the widget, see my original post on Reflex Feedback.

jQuery toggle button

On most of the mobile platforms you’ve probably seen a toggle, switch-style, button used as a replacement for a checkbox. I took a stab at doing something similar in HTML, CSS and Javascript.

toggle button

You can see the final result here (it’s a pain in the ass to embed it)

Note that while I used jQuery, this is not a jQuery extension. It doesn’t use that much jQuery and I really don’t get the desire to make everything-and-the-kitchen-sink a jQuery plugin.

The button depends upon 2 images, a base, containing the design and both states of the button:

toggle button base

… and a frame (optional if you can get away with using CSS borders):

toggle button frame

(note, the middle is transparent, not white)

The HTML and CSS consists of a:

  • A div, which has the its background-image set to the base and sized to the button’s inner area, roughly half the width (in this case, plus a few pixels as some pixels were shared by both states of the button) of the base and the same height
  • A block-level anchor element within the div, which has its background-image set to the frame and sized to the same area as the frame image. The anchor allows the area to be clickable and we’ll respond to the click event that occurs on this element.
  • An input checkbox which will store the checked/unchecked state of the button.
<div style="margin:0; padding:0; background:url(base.png) -41px 1px no-repeat transparent; width:46px; height:20px;">
<a class="toggle-button" href="#" style="margin:0; padding:0; display:block; background:url(frame.png) 0 0 no-repeat transparent; width:48px; height:20px;">
<input style="display:none;" type="checkbox" />
</a>
</div>

The Javascript code to handle the click event, where the background is shifted left or right when the button’s state is toggled using jQuery’s animate function,

$('.toggle-button').click(function ()
{
if (!$('input', this).is(':checked')) {
$(
this).parent().animate({ "background-position": "0px 1px" }, "slow");
$(
'input', this).attr('checked', true);
}
else {

$(
this).parent().animate({ "background-position": "-41px 1px" }, "slow");
$(
'input', this).attr('checked', false);
}

return false;
});

This all works great, but it’s not-so-great as a reusable component, so I encapsulated the code so that I could easily transform a div, such as the one shown below, into the toggle button.

<div id="my_toggle_button"></div>

Central to this is creating a ToggleButtonFactory, which will make the button by inserting the necessary HTML/CSS code into the DOM and bind the anchor to the click event. There’s also a ToggleButton object created by the factory which will have methods to toggle the button state (.toggle) and get the state of the button (.val).

function ToggleButton(_element, _funcSelectYes, _funcSelectNo)
{
this.jqDomElement = _element;
this.funcSelectYes = _funcSelectYes;
this.funcSelectNo = _funcSelectNo;

this.val = function ()
{
return $(this.jqDomElement).find('input').is(':checked');
}

this.toggle = function (funcSelectYes, funcSelectNo)
{
if (!this.jqDomElement.find('input').is(':checked')) {
this.jqDomElement.animate({ "background-position": "0px 1px" }, "slow");
this.jqDomElement.find('input').attr('checked', true);

if (this.funcSelectYes) {
this.funcSelectYes();
}
}
else {

this.jqDomElement.animate({ "background-position": "-41px 1px" }, "slow");
this.jqDomElement.find('input').attr('checked', false);

if (this.funcSelectNo) {
this.funcSelectNo();
}
}
}
}

var ToggleButtonFactory = {};
ToggleButtonFactory.makeButton =
function (element, initialState, funcSelectYes, funcSelectNo)
{
if ($(element).is('div')) {

var elemId = $(element).attr('id');
var newDivId = '__toggle_button_div_' + Math.ceil((Math.random() * 100000));
$(element).replaceWith(
'<div id="' + newDivId + '" style="margin:0; padding:0; background:url(base.png) -41px 1px no-repeat transparent; width:46px; height:20px;"><a class="toggle-button" href="#" style="margin:0; padding:0; display:block; background:url(frame.png) 0 0 no-repeat transparent; width:48px; height:20px;"><input id="' + elemId + '" name="' + elemId + '" style="display:none;" type="checkbox" /></a></div>');

var newElem = $('#' + newDivId);
var tb = new ToggleButton(newElem, funcSelectYes, funcSelectNo);

newElem.find(
'a').click(function ()
{
tb.toggle();
return false;
});

if (initialState) {
tb.toggle();
}

return tb;
}
}

Note there’s some additional code here to deal with setting the button to an initial state and callbacks for when the button is set to the “Yes” or “No” state.

Now, to transform the my_toggle_button div shown above into a toggle button, the following is done:

var btn = ToggleButtonFactory.makeButton('#my_toggle_button', false, function () { }, function () { });

(the call can be shorter, this shows calling with all arguments and capturing the return value [the ToggleButton object])

For another take on this, see the jQuery LightSwitch plugin.

Reflex Feedback widget

I worked on a small AJAX widget for user feedback built atop jQuery UI: Reflex Feedback. It’s inspired by the widgets you see from services like Get Satisfaction and UserVoice, but much simpler and it’s a frontend-only widget, how you handle the feedback info on the backend is up to you.

Here’s what it looks like.

reflex feedback widget dialog

And here’s what the tag that opens the dialog looks like:

reflex feedback widget tag

To use it, download or clone the ReflexFeedback repo from bitbucket

Place the .js file wherever you’d like but the /reflex.content folder should a subdirectory in the same folder as the page loading the .js file. Load reflex.js as you would any other javascript file:

<script type="text/javascript" src="js/reflex.js"></script>

Call Reflex.init() to add the widget to the page. The first argument is the DOM element to attach the additional HTML/CSS code to. The seconds argument is the server-side script to call when the user clicks Send Feedback.

Reflex.init($('body'), 'controller/post_feedback.php');

That’s it for the frontend. You should see the tag show up in the right-hand corner and when clicked the dialog open.

For the backend, the AJAX call to send the feedback info will send a POST request with 2 fields: feedback_type, feedback_txt.

Reflex expects an XML reply from the server:

<reflex>
<result>ok</result>
</reflex>

ok indicates a successful result, any other reply is considered an error.

A successful result will close the dialog and show another with a thank you message.

reflex feedback thank you dialog

For an error, a message is shown below the Send Feedback button, informing the user that an error has occurred and to try again.

reflex feedback send fail

As for what to actually do with the feedback, that’s up to you, but what I’m doing is sending myself an email with the feedback info. I’ve posted my PHP script below; feel free to use it, modify it, etc. If you do use this code, be sure to fill in your mail server credentials and a from address; you’ll also need PEAR’s Mail package installed.

<?php

require_once "Mail.php";
require_once "Mail/mime.php";

header('Content-type: application/xml; charset=utf-8');
echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n";

if(!isset($_POST['feedback_type']) || !isset($_POST['feedback_txt']))
{
echo "<reflex><result>error:missing-arguments</result></reflex>";
}
else
{
$from = "...";
$to = "...";
$subject = "Feedback from user...";

$feedback_type = $_POST['feedback_type'];
$feedback_txt = $_POST['feedback_txt'];

$bodyHtml = "<html><body>";
$bodyHtml .= "<p>Type: {$feedback_type}</p>";
$bodyHtml .= "<p>Feedback: {$feedback_txt}</p>";
$bodyHtml .= "</body></html>";
$body = $bodyHtml;

$host = "...";
$port = "...";
$username = "...";
$password = "...";

$headers = array('MIME-Version' => '1.0rn',
'Content-type' => 'text/html; charset=utf-8',
'From' => $from, 'To' => $to, 'Subject' => $subject);


$smtp = Mail::factory('smtp',
array ('host' => $host,
'port' => $port,
'auth' => true,
'username' => $username,
'password' => $password));

$mail = $smtp->send($to, $headers, $body);

if (PEAR::isError($mail))
{
$err_details = $mail->getMessage();
echo "<reflex><result>error:send-failure</result><details>{$err_details}</details></reflex>";
}
else
{
echo "<reflex><result>ok</result></reflex>";
}
}

?>

That’s all for now. I’ll work on more features and options for customization in the future. You can see the widget in action over at dotspott.com

hotspotdot

hotspotdot is my entry into Microsoft’s My App is Better Challenge. In a nutshell, hotspotdot allows you to locate and tag “hot spots” that are important to you, allowing you to create a personal database of hot spots.

It’s a very simple app, utilizing PHP, SQL Server 2008, jQuery, jQuery UI, and the Google Maps API. I wrapped thing up in under 2 weeks, but in order to make the August 25th deadline there were a few features I didn’t get a chance to put in, such as preloaders, icons on the map, and searching by tag. I’ll probably devote some time to these in the coming weeks.

hotspotdot login

The goal of the contest was to create something showing off the power of SQL Server and Microsoft’s new PHP for SQL Server 2.0 drivers. I used the new PDO driver which was pretty sweet – very simple, elegant API. Two things in particular that impressed me were transactions (very nice for doing multiple inserts or deletions) and a consistent and well designed exception model (makes error handling much easier and especially powerful when combined with transactions as failed queries don’t effect the database [code jumps to exception handler before commit() is called], so no chance of junk being inserted). Error messages, in general, were also much more descriptive than those I’ve encountered with MySQL.

Finding a server proved difficult. I got a shared hosting solution, but the host was unable (though I suspect unwilling) to install the PHP for SQL drivers. So I ended up getting a virtual private server, but this only came with SQL Server Express and I used some features (see below) that prevented a migration. In order to avoid purchasing an SQL Server 2008 license (way out of my budget), my final solution was use the shared hosting server for the DB and the VPS for everything else. Hence the reason for the site being located at the my. subdomain, which maps to the IP of the VPS.

SQL Server 2008 is a fine system (despite some annoyances with the management studio)… though it’s really just a solid database system, which isn’t bad, but there’s nothing really impressive or creative about that. It’s not really leaps and bounds above a cheap solution like MySQL. The contest was about SQL Server, so I tried to do something that utilized a fairly unique aspect of the system: the geography data type. I wasn’t too impressed. It simply holds a (longitude, latitude) pair in a certain format, nothing more. The one big advantage of having a vector type like this would be doing comparisons based on distance but, as far as I could tell, this isn’t supported (my queries failed). Worse yet, in SQL Server Management Studio the display of the geography type is in hexadecimal, making things very cryptic… I couldn’t help but wonder why this is any better than using 2 columns and storing the longitude and latitude values independently. That said, the idea of richer/more-complex data types within a relational database is a pretty cool idea and it would interesting to see it taken further and beyond its current, primitive state.

hotspotdot map

hotspotdot map

Leave a comment if you find a bug or have any questions, comments, etc.

Cloning and changing the TYPE attribute of an INPUT element

Cool snippet I found here, which was helpful in building Ekkio’s login forms.

ekkio login form

Notice that the password field is really a text field when the page loads – it does not mask characters. However, once the the field has focus and the user beings typing his password, masking is expected, so the field is transformed into a password field. This is trickier than it sounds because you can’t change the type attribute of an input element.

A possible solution is to clone the input element, then change the type attribute of the clone.

var clone = $('#login_field_password').clone(); clone.attr('type', 'password'); clone.attr('id', 'new_login_field_password'); clone.insertBefore($('#login_field_password')); $('#login_field_password').remove(); clone.attr('id', 'login_field_password'); $('#login_field_password').val(''); $('#login_field_password').css('color', '#000'); $('#login_field_password').focus();

You clone the field, change the type attribute of the clone, then delete the original. Unfortuantely, this doesn’t work in IE, as even changing the type attribute of a clone is not allowed. The only solution I’ve found so far is to clone manually by actually writing the HTML markup with the new type:

var clone = $('<input id="new_login_field_password" name="password" type="password" />');

This works but it’s not as versatile. The revised code is shown below.

var clone = $('<input id="new_login_field_password" name="password" type="password" />'); clone.attr('id', 'new_login_field_password'); clone.insertBefore($('#login_field_password')); $('#login_field_password').remove(); clone.attr('id', 'login_field_password'); $('#login_field_password').val(''); $('#login_field_password').css('color', '#000'); $('#login_field_password').focus();

XML DOM object to string conversion

One of those simple things which has eluded me for far too long. I finally found the solution here:

var string = (new XMLSerializer()).serializeToString(xmlobject); alert(string);

While the above is good to know, I’ve also discovered I don’t need it. I was looking to convert an XML DOM object, from an AJAX call, to plain text, but the XMLHttpRequest.responseText property provides just that. For jQuery users like myself, the XMLHttpRequest can be access via a synchronous jQuery.ajax call as the return value, or asynchronously via. the jQuery.ajax.complete callback or the jQuery.ajax.success callback (as of jQuery 1.4 the XMLHttpRequest object is now the third parameter).