Comments (11)
Is there any simple way to signal DT (...) using the enter key (for keyboard accessbility) (...) but from Javascript?
Have you read and search "accessibility" about datatables.net ? If it's not possible in datatables.net, it will be difficult to implement it in DT, IMHO.
- https://datatables.net/forums/discussion/5256/accessibility-for-keyboard-only-users
- http://regulus.datatables.net/forums/discussion/24882/make-datatable-colvis-accessible-using-keyboard-for-accessibility
- https://datatables.net/extensions/buttons/examples/initialisation/keys.html
- https://www.datatables.net/forums/discussion/34526/integration-of-select-and-keytable-with-keyboard-navigation
- https://stackoverflow.com/questions/30395489/jquery-datatables-accessibility
Hi @philibe ,
Thanks for jumping in here!
I had already perused a good number of those articles you suggested and just took a look at the rest. Part of the partial solution above was built based on the last SO post linked.
I'm not looking for full scale accessibility via Datatables, just to mimic the DT click to select functionality but bound to the enter key.
I've picked up the enter keypress and can add the .selected
class to the tr
but looking through the DT HTML widgets js suggests that there's more to emulating a single click select than just setting the class.
I'm looking to replicate that logic with JavaScript in the callback.
Does that clarify the scope of the issue?
from dt.
For whomever is also searching for a solution to this, a partial solution is detailed below. A JS file needs to be inserted as a script, either with includeScript
or in your www
folder.
The script needs to have the following:
Javascript
/**
* Return an array of all matching indices
*
* @param {Array} arr to be matched against
* @param {Function} fn of function that returns {Logical}
* @return {Array} of matching indexes
*/
function tagMatch(arr, fn) {
let idx = [];
if (arr instanceof jQuery) {
idx = arr.map((i, e) => {
let out = null;
if (fn(e)) {
out = i;
}
return out;
});
idx = Array.from(jqueryObjsOnly(idx));
} else {
idx = arr.reduce(function(a, e, i) {
if (fn(e))
a.push(i);
return a;
}, []);
}
return idx;
}
/**
* @param {Object} jq - A jQuery DOM object
* @returns {Array} stripped of jQuery methods
*/
function jqueryObjsOnly(jq) {
var out = [];
for (i = 0; i < jq.length; i++) {
out.push(jq[[i]]);
}
return out;
}
/**
* Make a DT keyboard accessible by enabling return key to select, populates the `_row_last_clicked` and the `_rows_selected` Shiny inputs
*
* @param {DataTable} table A DataTable object, passed in via the callback automatically.
*/
/**
* Make a DT keyboard accessible by enabling return key to select, populates the `_row_last_clicked` and the `_rows_selected` Shiny inputs
*
* @param {DataTable} table A DataTable object, passed in via the callback automatically.
*/
function dt_tab_accessible(table) {
table.on('key', function(e, datatable, key, cell, originalEvent){
var table_id = cell.table().node().id;
var shiny_id = $('#' + table_id).parent().parent().attr('id');
if (key == 13){
// When return is pressed
// Select the row
let row_idx = cell.index().row;
let tr = $(datatable.row(row_idx).node())
if (tr.hasClass("selected")) {
tr.removeClass('selected');
} else {
tr.addClass("selected")
}
// TODO how to set class on tr, update crosstalk inputs?
// Update the Shiny inputs
// row_last_clicked
Shiny.setInputValue(shiny_id + '_row_last_clicked', row_idx, {priority: 'event'})
// rows_selected
let trs = tr.parent("tbody").children('tr');
let sel_idx = tagMatch(trs, (e)=> {return $(e).hasClass('selected')});
let rows_selected = sel_idx.map(i => i+1)
Shiny.setInputValue(shiny_id + '_rows_selected', rows_selected, {priority: 'event'})
}
});
}
the R app looks like this, where the argument to includeScript
is the path to your JS script.
R
library(shiny)
library(DT)
devtools::load_all()
ui <- fluidPage(
shiny::includeScript(UU::path_strip_shiny(dirs$js("custom_fns.js"))),
title = 'Select Table Rows',
h1('A Server-side Table'),
fluidRow(
column(9, DT::dataTableOutput('x3')),
column(3, verbatimTextOutput('x4'))
)
)
server <- function(input, output, session) { output$x1 = DT::renderDataTable(cars, server = FALSE)
# highlight selected rows in the scatterplot
output$x2 = renderPlot({
s = input$x1_rows_selected
par(mar = c(4, 4, 1, .1))
plot(cars)
if (length(s)) points(cars[s, , drop = FALSE], pch = 19, cex = 2)
})
# server-side processing
mtcars2 = mtcars[, 1:8]
output$x3 = DT::renderDataTable({
DT::datatable(
mtcars2,
callback = DT::JS("
dt_tab_accessible(table);
"),
extensions = "KeyTable", # Enable tabindexes,
options = list(keys = TRUE) # Enable tabindexes
)
}, server = TRUE)
# print the selected indices
output$x4 = renderPrint({
s = input$x3_rows_selected
if (length(s)) {
cat('These rows were selected:\n\n')
cat(s, sep = ', ')
}
})
}
shinyApp(ui, server)
This enables the bare minimum functionality of using the Enter
key to "select" rows which will populate the _row_last_clicked
and the _rows_selected
Shiny inputs with the table selections. Limitations: crosstalk functionality is omitted, so it will not update selections in crosstalk. _cells_clicked
input is not updated.
from dt.
You want to use the server-side feature?
from dt.
Hi @stla,
The typical DT
selection, not the one enabled by the Select
extension, if that's what you're getting at?
from dt.
@stla,
It looks like changeInput
is involved in setting the Shiny values
DT/inst/htmlwidgets/datatables.js
Line 917 in 487dc08
selectedRows
is used to create an index of selected rows (dependent on them having the .selected
class):
var selectedRows = function() {
var rows = table.rows('.' + selClass);
var idx = rows.indexes().toArray();
if (!server) {
selected1 = addOne(idx);
return selected1;
}
idx = idx.map(function(i) {
return DT_rows_current[i];
});
selected1 = selMode === 'multiple' ? unique(selected1.concat(idx)) : idx;
return selected1;
}
selectRows
appears to be involved in adding remove rows:
DT/inst/htmlwidgets/datatables.js
Line 1103 in 487dc08
The declaration of these inline functions has them inheriting a lot of variables from their parent scope though
from dt.
It looks like
changeInput
is involved in setting the Shiny values
@yogat3ch Yes, if you want to pass a value from JavaScript to R, Shiny.setInputValue()
is the way to go. I talked about this a number of years ago: https://slides.yihui.org/2016-Shiny-DT-Yihui-Xie.html#(7)
Official documentation: https://shiny.posit.co/r/articles/build/communicating-with-js/
from dt.
Thanks @yihui & @stla , I think I've gotten that far. Here's a reprex with what's implemented thus far, with some interspersed pseudo-code with the outstanding questions:
library(shiny)
library(DT)
ui <- fluidPage(
title = 'Select Table Rows',
h1('A Server-side Table'),
fluidRow(
column(9, DT::dataTableOutput('x3')),
column(3, verbatimTextOutput('x4'))
)
)
server <- function(input, output, session) { output$x1 = DT::renderDataTable(cars, server = FALSE)
# highlight selected rows in the scatterplot
output$x2 = renderPlot({
s = input$x1_rows_selected
par(mar = c(4, 4, 1, .1))
plot(cars)
if (length(s)) points(cars[s, , drop = FALSE], pch = 19, cex = 2)
})
# server-side processing
mtcars2 = mtcars[, 1:8]
output$x3 = DT::renderDataTable({
DT::datatable(
mtcars2,
callback = DT::JS("
function dt_tab_accessible(table) {
table.on('key', function(e, datatable, key, cell, originalEvent){
var table_id = cell.table().node().id;
var shiny_id = $('#' + table_id).parent().parent().attr('id');
if (key == 13){
debugger;
// When return is pressed
// Select the row
let row_idx = cell.row().index() + 1;
// TODO how to set class on tr, update crosstalk inputs?
// Update the Shiny inputs
// row_last_clicked
Shiny.setInputValue(shiny_id + '_row_last_clicked', row_idx, {priority: 'event'})
// rows_selected
// TODO How to get currently selected rows as numeric index?
let rows_selected = undefined;
Shiny.setInputValue(shiny_id + '_rows_selected', rows_selected, {priority: 'event'})
}
});
}
dt_tab_accessible(table);
"),
extensions = "KeyTable", # Enable tabindexes,
options = list(keys = TRUE)
)
}, server = TRUE)
# print the selected indices
output$x4 = renderPrint({
s = input$x3_rows_selected
if (length(s)) {
cat('These rows were selected:\n\n')
cat(s, sep = ', ')
}
})
}
shinyApp(ui, server)
from dt.
Is the .selected
class what signals to DT that a row is selected? If I just add the .selected
class to the associated tr
tag on each click, would the table.rows({selected: true})
return all the currently clicked/selected rows?
from dt.
It looks like DT
doesn't classify a row as selected if it has the .selected
class, I added it to the clicked row using JQuery
and this is what it the rows({selected: true})
method returns:
Does this matter? Could I use Jquery to get the indices of the rows with .selected
and just use those as the _rows_selected
? Would there be a risk of the DT
heuristic for selected rows desynchronizing from the Shiny inputs here?
I'm a little hesitant to override the Shiny inputs with this approach because I think subsequent mouse clicks may compute selected rows differently, and would rather just cue DT
using Javascript to run it's own code for "selecting" a row if thats a safer approach?
from dt.
Thoughts on the above @stla & @yihui?
Is there any simple way to signal DT
to highlight a row when using the enter key (for keyboard accessbility), mimicking a mouse click, but from Javascript? That seems like the safest approach but I'm not sure it's feasible...
from dt.
Is there any simple way to signal DT (...) using the enter key (for keyboard accessbility) (...) but from Javascript?
Have you read and search "accessibility" about datatables.net ? If it's not possible in datatables.net, it will be difficult to implement it in DT, IMHO.
- https://datatables.net/forums/discussion/5256/accessibility-for-keyboard-only-users
- http://regulus.datatables.net/forums/discussion/24882/make-datatable-colvis-accessible-using-keyboard-for-accessibility
- https://datatables.net/extensions/buttons/examples/initialisation/keys.html
- https://www.datatables.net/forums/discussion/34526/integration-of-select-and-keytable-with-keyboard-navigation
- https://stackoverflow.com/questions/30395489/jquery-datatables-accessibility
from dt.
Related Issues (20)
- `renderDT()` evaluates `...` in `expr`'s `env`
- Unable to test `renderDT()` in a `MockShinySession`
- First option entry ignored for default search values per column HOT 3
- "locked" proxy structure not accommodating new data HOT 4
- Extract search functionality
- upgrade to datatables v2 HOT 2
- R Shiny application designed to navigate through multiple sections within a single page
- Utilizing Scroller DataTable extension with `datatable(fillContainer = TRUE, ...)`
- Enable percentBar plugin HOT 5
- Upgrade DataTables to v1.13.11 HOT 2
- Interaction between searching options parameter and `datatable::filter` HOT 2
- Possible bug: styleRow() appears to do nothing when viewed in browser
- Low text contrast with pkgdown dark theme HOT 2
- Official documentation site throws an AJAX error HOT 1
- rowreorder glitch
- How to use differrent Datatable option in R/Shiny ? HOT 4
- `clearSearch` sends multiple copies of the table to the client
- Capturing `input$*_cell_edit` of a top level-rendered `DT` for observation/reaction in a module namespace?
- Uncaught TypeError: array[0].map is not a function HOT 1
- How to combine RowGroup and expandable subtable ?
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from dt.