AJ.
AJ.

Reputation: 16729

How can I add a jQuery UI icon to a dynamically generated button?

When downloading jQuery UI you get a stylesheet for whatever theme was selected, as well as several image files containing icons. I've figured out how to add an icon to a single <button> element, but I have a situation where I'm dynamically generating buttons in a grid (jqGrid) and want to use these icons. So, say I want to use this icon from the CSS file:

.ui-icon-trash { background-position: -176px -96px; }

Then, I add the buttons to the grid by handling the gridComplete event:

gridComplete: function () {
    var ids = $("#myGrid").jqGrid('getDataIDs');
    for (var i = 0; i < ids.length; i++) {
        var deleteButton = "<button type='button' style='height: 22px;width: 20px;' title='Delete' onclick=deleteRow(" + ids[i] + ")></button>";
        $("#myGrid").jqGrid('setRowData', ids[i], { DeleteButton: deleteButton });
    }
}

I've tried using a class in the button tag, for example, deleteRowButton, and then using jQuery like this:

$(".deleteRowButton").button({
    icons: {
        primary: 'ui-icon-trash'
    },
    text: false
});

But this doesn't work. What do I have to do to get my buttons to have this icon?

Upvotes: 2

Views: 3457

Answers (2)

Oleg
Oleg

Reputation: 222017

I suppose that your code with $(".deleteRowButton").button({icons: {primary: 'ui-icon-trash'}, text: false}); didn't worked because you placed it in the wrong place. If you create the <button class='deleteRowButton' ...> inside of gridComplete you should make the call $(".deleteRowButton").button(...) also inside of gridComplete directly after the code which you posted:

gridComplete: function () {
    var $this = $(this), ids = $this.jqGrid('getDataIDs'), l = ids.length,
        i, deleteButton;
    for (i = 0; i < l; i++) {
        deleteButton = "<button type='button' style='height:22px;width:20px;'" +
            " class='deleteRowButton' title='Delete' onclick=deleteRow(" +
            ids[i] + ")></button>";
        $this.jqGrid('setRowData', ids[i], { DeleteButton: deleteButton });
    }
    $(".deleteRowButton").button({
        icons: {
            primary: 'ui-icon-trash'
        },
        text: false
    });
}

see the first demo.

The small problem exists in the performance of the above approach. Using setRowData you make changes on the page. Every change on the page follow recalculation of positions of all other elements existing on the page. So to improve performance it's recommended to reduce the number of changes on the grid. So the better way is the usage of custom formattrer. The new version of the code will be practically exactly so easy as the previous one. You just need to define formatter as function:

{ name: 'DeleteButton', width: 20,
    formatter: function (cellvalue, options) {
        return "<button type='button' class='deleteRowButton' " +
            "style='height: 22px;width: 20px;' title='Delete'></button>";
    }},

and reduce the code of gridComplete or loadComplete to

gridComplete: function () {
    $(".deleteRowButton").button({
        icons: {
            primary: 'ui-icon-trash'
        },
        text: false
    }).click(function (e) {
        var $tr = $(e.target).closest("tr.jqgrow");
        alert("the row with id=" + $tr.attr("id") + " need be deleted");
    });
}

In your original code the method deleteRow must be global (it should be defined on the top level). The new code can use just click event handler. See the next demo.

By the way you don't really need to bind every <button> to click event handler. As well known if there are no click event handler on the button the event bubbling will take place. So instead of binding click event handler every time on loading and reloading of the grid one can just define once the corresponding event handler on the whole grid body. In other words you can use onCellSelect callback. the usage is very comfortable because the rowid and the index of the column of the clicked cell are already calculated. Moreover per 4-th parameter e of the onCellSelect callback you can access to the event handler where e.tagret is the DOM element of the clicked <button>. So you can replace the above code of gridComplete to the following code:

onCellSelect: function (rowid, iCol, cellcontent, e) {
    if ($(e.target).closest("button.deleteRowButton").length > 0) {
        alert("the row with id=" + rowid + " need be deleted");
    }
},
gridComplete: function () {
    $(".deleteRowButton").button({
        icons: {
            primary: 'ui-icon-trash'
        },
        text: false
    });
}

In the way you can more improve the performance and reduce a little the memory used for the page. The demo shows the last code live. In the most cases you don't need to use constructs like $(e.target).closest("button.deleteRowButton").length > 0. Instead of that you can just verify the column index iCol. If you need you can test the column name instead. You can use

$(this).jqGrid("getGridParam", "colModel")[iCol].name

to convert the iCol to the corresponding column name.

Upvotes: 4

FrankieAvocado
FrankieAvocado

Reputation: 343

I'd suggest switching from 'button' to 'input type="button"' You should be able to use the background image in CSS to set an icon. Your grid complete function would look something like this:

gridComplete: function () {    
var ids = $("#myGrid").jqGrid('getDataIDs');    
for (var i = 0; i < ids.length; i++) {    
    var deleteButton = "<input type='button' class='HasIcon' style='height: 22px;width: 20px;' title='Delete' onclick=deleteRow(" + ids[i] + ")/>";    
    $("#myGrid").jqGrid('setRowData', ids[i], { DeleteButton: deleteButton });    
}    

}

and your CSS would look like this:

#myGrid input[type=button].HasIcon
{
    background-image: url(/* icon location */);
    background-repeat: no-repeat;
    text-align: center;
    padding-left: 20px; /* slightly longer than your icon */
}

You won't need to use jquery to apply the icon, since CSS will do that for you. The magic of CSS triumphs again! :-)

Upvotes: 1

Related Questions