jqGrid: inline-edit whole row, but focus on cell clicked to enter edit mode
Our web application uses jqGrid for data entry, and data is edited inline and in rows.
A customer wishes a more "Excel-like" experience so that when switching a row to inline editing by clicking on a cell, that cell receives the focus (currently, the first cell in the row gets the开发者_StackOverflow社区 focus).
I ran into the same issue. Very frustrating. I modified the jquery.jqGrid.js file to accommodate setting focus on the cell clicked on.
In my editRow function I added the following code:
function editRow(id, row, col, z)
{
grid.jqGrid("editRow", id, true, function ()
{
var f = $("input, select", z.target).focus();
return f;
});
}
This will create a variable f that will ultimately be passed to the "oneditfunc" in $.jgrid.extend
The problem is in the setTimeout function.
$("td:eq("+focus+") input",ind).focus();
as this gets executed with focus set to the first editable field from the .each function above. This would be a great place to pass in a focus index, but not possible.
// End compatible
return this.each(function ()
{
var $t = this, nm, tmp, editable, cnt = 0, focus = null, svr = {}, ind, cm, bfer;
...
I then added the following code. (The line with >>> is unchanged. It is only there to help you find the insertion point in the code.)
>>> $($t).triggerHandler("jqGridInlineEditRow", [rowid, o]);
if ($.isFunction(o.oneditfunc))
{
// modified to allow for setting focus on the
// field clicked on
// sets a return value. this was added to the original code. if using
// legacy code, should see no change as r will be undefined
var r = o.oneditfunc.call($t, rowid);
// if get a return value, this indicates you want to set focus
if (r && r[0])
{
var focusIndex = focus;
var i = 0;
// look for the field name that was clicked on
// cm, which is built above, has no index value associated
// with it, so we must keep track of
var focusField = $.grep(cm, function(c)
{
// find the field name which was clicked on
if (c.name == r[0].name)
focusIndex = i;
i++;
return c.name == r[0].name;
});
// if the field is editable, then update focus index
// which is defined above
if (focusField[0].editable)
{
focus = focusIndex;
}
}
}
The most elegant solution? Probably not, but it does allow all legacy code to work and figure out which field was clicked on so can set focus
Unfortunately the grid can only set focus to the first editable field in the row. You can see this in the source code of grid.inlinedit.js. Here it figures out the index of the row to set focus to, by iterating over them and finding the first one:
if(cm[i].editable===true) {
if(focus===null) { focus = i; }
and later on it sets focus:
$("td:eq("+focus+") input",ind).focus();
Which selects the element at the index stored in the focus
variable - for reference, see the :eq selector docs.
That said, if you are so inclined you could edit this section of code to add your own custom logic to control which element receives focus...
I agree with Justin that jqGrid has no direct support of the behavior which you need. The way ich which you can receive which is need can be more complex as just one option of jqGrid. Look at the code from the answer. I hope it will help you.
This is serious deficiency in jqGrid. If grid is wider than window, you also need to scroll right to make clicked cell visible. For this you can use code from http://www.trirand.com/blog/?page_id=393/help/inline-edit-problem-with-scrolling/
if (rowID && rowID !== lastSelectedRow) {
var scrollPosition = 0;
scrollPosition = grid2.closest(".ui-jqgrid-bdiv").scrollLeft();
grid2.jqGrid('restoreRow', lastSelectedRow);
grid.jqGrid('editRow', rowID, true);
lastSelectedRow = rowID;
setTimeout(function(){
grid2.closest(".ui-jqgrid-bdiv").scrollLeft(scrollPosition);
},100);
For my part, i used both Justin Ethier and Steve's answers to come up with my own solution (for jqGrid 4.4.3):
In the source of jqGrid, i just comment out the following line (this is to prevent the first input of the edited row to receive the focus):
// commented out
// $("td:eq("+focus+") input",ind).focus();
Then, I create a global js variable that will hold the source of the click on the grid:
var clickedCell;
And finally, set that variable when clicking on the cell.
$('#myJqGrid td').on('click', function(e){ clickedCell = this; });
In order to be able to attach the event to the table cell though, we need to be sure the grid has been created, so it has to be done in the "gridComplete" function of the jqGrid, e.g.:
$('#myJqGrid').jqGrid('setGridParam', {
gridComplete: function(id){
$('#myJqGrid td').on('click', function(e){ clickedCell = this; });
}
});
And finally, when editing the row, i retrieve the clicked cell (saved in the variable "clickedCell"), and give the focus to the input or textarea inside it. This has to be done in the "onSelectRow" function of the jqGrid:
$('#myJqGrid').jqGrid('setGridParam',
{
onSelectRow : function(id) {
...
//switching the selected row to editmode
$('#myJqGrid').jqGrid(
'editRow',
id,
{
keys: true,
// when editing the row, give the focus to the input
//or textarea within the last clicked cell
oneditfunc: function() {
$('input, textarea', clickedCell).focus();
}
);
...
}
}
});
The only problem is there really isn't a solution without touching even slightly the source code of jqGrid. If you don't, it will still work, but the first input will still get the focus at first, shifting the horizontal scroll of your page, even after the correct focus has been assigned.
I posted this answer in another stackoverflow related question. Hope this will apply to you.
I somehow succeeded to accomplish this by attaching a dblclick event to every td of the table, I know this is not the best method, but you are free to optimize it how you like, you can also ignore the setTimeout which was used only for testing.
$("#jqGrid").on("dblclick", "td", function (event) {
// setTimeout(function () {
console.log(this);
$(event.target).find("input,select").focus();
// }, 0);
});
Hope this will help you.
精彩评论