Prevent Mysterious String-to-Number Conversion of Color Data

Problem

HTML:

<form method="post" action="">
        <select name="fancySelect" class="makeMeFancy">
            <option value="0" selected="selected" data-skip="1">Email Color Scheme</option>
            <option value="81" data-color1="993300" data-color2="000000" data-html-text="Brown on Black">Brown on Black</option>
            <option value="77" data-color1="663366" data-color2="000000" data-html-text="Purple on Black">Purple on Black</option>
            <option value="129" data-color1="00ccff" data-color2="000000" data-html-text="Teal on Black">Teal on Black</option>
            <option value="75" data-color1="666666" data-color2="000000" data-html-text="Dark Gray on Black">Dark Gray on Black</option>
            <option value="85" data-color1="999999" data-color2="000000" data-html-text="Gray on Black">Gray on Black</option>
            <option value="81" data-color1="993300" data-color2="000000" data-html-text="Brown on Black">Brown on Black</option>
            <option value="77" data-color1="663366" data-color2="000000" data-html-text="Purple on Black">Purple on Black</option>
            <option value="129" data-color1="00ccff" data-color2="000000" data-html-text="Teal on Black">Teal on Black</option>
            <option value="75" data-color1="666666" data-color2="000000" data-html-text="Dark Gray on Black">Dark Gray on Black</option>
            <option value="85" data-color1="999999" data-color2="000000" data-html-text="Gray on Black">Gray on Black</option>
            <option value="81" data-color1="993300" data-color2="000000" data-html-text="Brown on Black">Brown on Black</option>
            <option value="77" data-color1="663366" data-color2="000000" data-html-text="Purple on Black">Purple on Black</option>
            <option value="129" data-color1="00ccff" data-color2="000000" data-html-text="Teal on Black">Teal on Black</option>
            <option value="75" data-color1="666666" data-color2="000000" data-html-text="Dark Gray on Black">Dark Gray on Black</option>
            <option value="85" data-color1="999999" data-color2="000000" data-html-text="Gray on Black">Gray on Black</option>
        </select>
    </form>

jQUERY:

$(document).ready(function(){

    // The select element to be replaced:
    var select = $('select.makeMeFancy');

    var selectBoxContainer = $('<div>',{
        width       : select.outerWidth(),
        className   : 'tzSelect',
        html        : '<div class="selectBox"></div>'
    });

    var dropDown = $('<ul>',{className:'dropDown'});
    var selectBox = selectBoxContainer.find('.selectBox');

    // Looping though the options of the original select element

    select.find('option').each(function(i){
        var option = $(this);

        if(i==select.attr('selectedIndex')){
            selectBox.html(option.text());
        }

        // As of jQuery 1.4.3 we can access HTML5 
        // data attributes with the data() method.

        if(option.data('skip')){
            return true;
        }


        // Creating a dropdown item according to the
        // data-icon and data-html-text HTML5 attributes:

        var li = $('<li>',{
            html:   '<table border="0" cellspacing="3" cellpadding="3"><tr valign="middle"><td bgcolor="#'+option.data(String('color1'))+'" class="color">&nbsp;</td><td bgcolor="#'+option.data(String('color2'))+'" class="color">&nbsp;</td><td class="text"><span>'+option.data('html-text')+'</span></td></tr></table>'
        });

        li.click(function(){
            selectBox.html("<div id='selected-colors'><div id='color-selection1' style='background:#"+ option.data(String('color1')) +";'></div><div id='color-selection2' style='background:#"+ option.data(String('color2')) +";'></div><span>"+option.data('html-text')+"</span>");
            dropDown.trigger('hide');

            // When a click occurs, we are also reflecting
            // the change on the original select element:
            select.val(option.val());

            return false;
        });

        dropDown.append(li);
    });

    selectBoxContainer.append(dropDown.hide());
    select.hide().after(selectBoxContainer);

    // Binding custom show and hide events on the dropDown:

    dropDown.bind('show',function(){

        if(dropDown.is(':animated')){
            return false;
        }

        selectBox.addClass('expanded');
        dropDown.slideDown();

    }).bind('hide',function(){

        if(dropDown.is(':animated')){
            return false;
        }

        selectBox.removeClass('expanded');
        dropDown.slideUp();

    }).bind('toggle',function(){
        if(selectBox.hasClass('expanded')){
            dropDown.trigger('hide');
        }
        else dropDown.trigger('show');
    });

    selectBox.click(function(){
        dropDown.trigger('toggle');
        return false;
    });

    // If we click anywhere on the page, while the
    // dropdown is shown, it is going to be hidden:

    $(document).click(function(){
        dropDown.trigger('hide');
    });
});

In the drop down of the option list, the BLACK in data-color-2 shows up fine, but is populated with a bgcolor of '0' instead of the 000000 that I have in the option data-color2. What do I need to add to the code to force that BOTH data-color1 and data-color2 are both STRINGS and not INTEGERS?

Problem courtesy of: Murphy1976

Solution

The jQuery .data() method intentionally transforms the attribute contents. It thinks it's doing you a favor, but often (as in your case) it's not.

You could add "#" to your color attributes. That'd cause the attempt to convert to a number to fail, and you'd end up with a string.

Alternatively, you could use a single attribute for both colors, in the form of a JSON literal:

 <option value="81" data-colors='{ "color1": "993300", "color2": "000000" }' data-html-text="Brown on Black">Brown on Black</option>

In the JavaScript code, you'd get an object and refer to the colors:

    var colors = option.data("colors");

Then colors.color1 and colors.color2 will be proper strings, because your JSON notation explicitly describes them as such.

edit — an insightful comment points out that the numeric conversion is only done when the result of the conversion is a number that, when stringified again, is the same as the original string.

edit again — The JSON needs to be valid, so the property names need to be in double quotes. My apologies.

Here is a working jsfiddle.

Solution courtesy of: Pointy

Discussion

There is currently no discussion for this recipe.

This recipe can be found in it's original form on Stack Over Flow.