Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to copy a table column name? #227

Open
Norlandz opened this issue Feb 6, 2024 · 8 comments
Open

How to copy a table column name? #227

Norlandz opened this issue Feb 6, 2024 · 8 comments

Comments

@Norlandz
Copy link

Norlandz commented Feb 6, 2024

How to copy a table column name?

The current behavior when click on the table column name triggers a sort.
Is it possible to allow something like alt+mouse drag,, to select & copy the name,, instead of triggering sort?

@mwouts
Copy link
Owner

mwouts commented Feb 6, 2024

Hey @Norlandz , that's a great question and actually I often wonder how to do that.

Your question is actually for the https://datatables.net/ project - the underlying JavaScript library that we use to display the tables.

Would you mind searching/asking for this on the datatables forum and keep us posted?

Thanks

@Norlandz
Copy link
Author

Norlandz commented Feb 7, 2024

@mwouts
I may look into it when I have time.
(Never tried this library in js before.)

Btw, as for css workaround user-select / pointer-events, I tried, seemed not working.

@mwouts
Copy link
Owner

mwouts commented Feb 10, 2024

Hey @Norlandz , possibly the most convenient workaround is to give a name to your index (see below).

Alternatively, the ordering component can be deactivated with columnDefs=[{"targets":"_all", "orderable": False}].

Let me know if that answers your question.

import pandas as pd
from itables import init_notebook_mode, show

init_notebook_mode(all_interactive=True)

# The column cannot be selected
df = pd.DataFrame({'column':[5]})
df

# Adding a named index lets you select the column (but not the index name)
df.index.name = 'index'
df

# Make all columns not orderable
show(df, columnDefs=[{"targets":"_all", "orderable": False}])

# Make all columns not orderable for all tables
import itables.options as opt
opt.columnDefs=[{"targets":"_all", "orderable": False}]

df

image

@Norlandz
Copy link
Author

Norlandz commented Feb 11, 2024

@mwouts Good to know, but this workaround is adding extra info to the df & require more visual space.

What I do instead is: inject a javascript
-- if you click on the column name -> it selects the column name
(but sorts it too... i could interrupt the click event of sort, but for simplicity i didnt) .
(Still, not the best though.)

jsscript = """
<script>

"use strict";
// hopefully, this will work without memory leak ...
console.log('inject js -- one click select table column name');
// function ready(fn) {
//   if (document.readyState !== 'loading') { fn(); return; }
//   document.addEventListener('DOMContentLoaded', fn);
// }
// ready(async function () {});
let mpp_eltTh_WithListener_prev = new Map();
// this requires constantly adding new listener to newly added elements (& discard old listeners)
document.addEventListener('click', function (ev) {
    // await new Promise((r) => setTimeout(r, 2000));
    const arr_elt = document.querySelectorAll('th');
    const mpp_eltTh_WithListener_new = new Map();
    for (const elt of arr_elt) {
        // console.log(elt);
        // if (!(elt instanceof HTMLTableCellElement)) throw new TypeError();
        const listener_prev = mpp_eltTh_WithListener_prev.get(elt);
        if (listener_prev === undefined) {
            // new element detected -- add new listener
            const listener_new = (ev) => {
                // https://developer.mozilla.org/en-US/docs/Web/API/Selection/addRange
                const selection = window.getSelection();
                if (selection === null)
                    return;
                if (selection.rangeCount > 0) {
                    selection.removeAllRanges(); // must dk .
                }
                const range = document.createRange();
                range.selectNode(elt);
                selection.addRange(range);
            };
            elt.addEventListener('click', listener_new);
            mpp_eltTh_WithListener_new.set(elt, listener_new);
        }
        else {
            // already have listener
            mpp_eltTh_WithListener_prev.delete(elt); // delete (exclude) from old map, so that in a later operation it wont be unregistered
            mpp_eltTh_WithListener_new.set(elt, listener_prev);
        }
    }
    // clear up old Map, replace with new Map -- remember to delete (exclude) retained elemnt first before go to this step
    for (const [elt_prev, listener_prev] of mpp_eltTh_WithListener_prev) {
        elt_prev.removeEventListener('click', listener_prev);
        //     []
        //     According to the jquery Documentation when using remove() method over an element, all event listeners are removed from memory. This affects the element it selft and all child nodes. If you want to keep the event listners in memory you should use .detach() instead
        //     <>
        //     https://stackoverflow.com/questions/12528049/if-a-dom-element-is-removed-are-its-listeners-also-removed-from-memory#:~:text=According%20to%20the%20jquery%20Documentation,detach()%20instead.
        // em // whatever
    }
    mpp_eltTh_WithListener_prev = mpp_eltTh_WithListener_new;
});

</script>
"""
display(HTML(jsscript))
original ts code
// hopefully, this will work without memory leak ...
console.log('inject js -- one click select table column name');

// function ready(fn) {
//   if (document.readyState !== 'loading') { fn(); return; }
//   document.addEventListener('DOMContentLoaded', fn);
// }
// ready(async function () {});

let mpp_eltTh_WithListener_prev = new Map<HTMLTableCellElement, (ev: MouseEvent) => void>();
// this requires constantly adding new listener to newly added elements (& discard old listeners)
document.addEventListener('click', function (ev) {
  // await new Promise((r) => setTimeout(r, 2000));
  const arr_elt = document.querySelectorAll('th');
  const mpp_eltTh_WithListener_new = new Map<HTMLTableCellElement, (ev: MouseEvent) => void>();
  for (const elt of arr_elt) {
    // console.log(elt);
    // if (!(elt instanceof HTMLTableCellElement)) throw new TypeError();
    const listener_prev = mpp_eltTh_WithListener_prev.get(elt);
    if (listener_prev === undefined) {
      // new element detected -- add new listener
      const listener_new = (ev) => {
        // https://developer.mozilla.org/en-US/docs/Web/API/Selection/addRange
        const selection = window.getSelection();
        if (selection === null) return;
        if (selection.rangeCount > 0) {
          selection.removeAllRanges(); // must dk .
        }
        const range = document.createRange();
        range.selectNode(elt);
        selection.addRange(range);
      };
      elt.addEventListener('click', listener_new);
      mpp_eltTh_WithListener_new.set(elt, listener_new);
    } else {
      // already have listener
      mpp_eltTh_WithListener_prev.delete(elt); // delete (exclude) from old map, so that in a later operation it wont be unregistered
      mpp_eltTh_WithListener_new.set(elt, listener_prev);
    }
  }
  // clear up old Map, replace with new Map -- remember to delete (exclude) retained elemnt first before go to this step
  for (const [elt_prev, listener_prev] of mpp_eltTh_WithListener_prev) {
    elt_prev.removeEventListener('click', listener_prev);
    //     []
    //     According to the jquery Documentation when using remove() method over an element, all event listeners are removed from memory. This affects the element it selft and all child nodes. If you want to keep the event listners in memory you should use .detach() instead
    //     <>
    //     https://stackoverflow.com/questions/12528049/if-a-dom-element-is-removed-are-its-listeners-also-removed-from-memory#:~:text=According%20to%20the%20jquery%20Documentation,detach()%20instead.
    // em // whatever
  }
  mpp_eltTh_WithListener_prev = mpp_eltTh_WithListener_new;
});


// how to let tsc use let instead of var in generated js
// ;wrong; h:\Using\t1-vite>npx tsc --target ES6 ./src/main.tsx
// h:\Using\t1-vite\compileThis>tsc -p tsconfig.json

Update: code modified for constant checking new table element

@mwouts
Copy link
Owner

mwouts commented Feb 11, 2024

Thanks @Norlandz ! It might make sense to ask for a fix in https://github.com/DataTables/DataTablesSrc, can I let you open an issue or even PR there if you feel that's the right move? Thanks

@Norlandz
Copy link
Author

Thanks @Norlandz ! It might make sense to ask for a fix in https://github.com/DataTables/DataTablesSrc, can I let you open an issue or even PR there if you feel that's the right move? Thanks

I didnt read into the API / source code of DataTables, (there might be api for picking the column name), not sure a issue / PR is appropriate.

The code above is just a trivial workaround. (If you want to use it anywhere its totally fine)

(I may not submit an issue for now until I read through the api in js in future, but if you want to do that instead its fine too.)
(You can close the issue here in the meantime if you want.)

@mwouts mwouts closed this as not planned Won't fix, can't repro, duplicate, stale Mar 5, 2024
@mwouts mwouts reopened this Apr 21, 2024
@mwouts
Copy link
Owner

mwouts commented Apr 21, 2024

I am reopening this issue as now with datatables==2.0.1 the index name trick does not work anymore.

@mwouts
Copy link
Owner

mwouts commented Apr 25, 2024

I got an answer and a working example on the datatables forum: https://datatables.net/forums/discussion/comment/231058

It involves setting a data attribute on the header cells (<th data-dt-order="icon-only">Name</th>) and then set custom listeners in Javascript, so that's possibly a bit more involved than what I can develop or maintain at the moment, but at least we have a path towards this. Also, according to the comments on the thread that might become easier in a future version of datatables.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants