61

I've got this problem where I need to show and hide divs when clicking on a table cell. However, I also want people to be able to select text and copy it within the cell without hiding the information.

Totally open to changing the design if necessary. :)

Here's a fiddle which demonstrates the issue

http://jsfiddle.net/k61u66ek/1/

Here's the HTML code in the fiddle:

<table border=1>
    <tr>
        <td>
            Information
        </td>
        <td onClick="toggleInfo()">
            <div id="information" style="display:none">
                More information that I want to select without hiding
            </div>
            <div id="clicktoshow">
                Click to show info
            </div>

        </td>
    </tr>
</table>

Here's the javascript:

function toggleInfo() {
    $("#clicktoshow").toggle();
    $("#information").toggle();    
}

Any suggestion/advise is much appreciated!

/Patrik

1

7 Answers 7

72

One option is to check the type of the Selection object returned by window.getSelection:

function toggleInfo() {
    var selection = window.getSelection();
    if(selection.type != "Range") {
        $("#clicktoshow").toggle();
        $("#information").toggle();
    }
}

http://jsfiddle.net/k61u66ek/4/

Update

If the browser you're targeting doesn't expose a type property on the Selection object then you can test against the length of the selected value instead:

function toggleInfo() {
    var selection = window.getSelection();
    if(selection.toString().length === 0) {
        $("#clicktoshow").toggle();
        $("#information").toggle();
    }
}

http://jsfiddle.net/k61u66ek/9/

which can in turn be reduced down to a bool check on toString:

if(!selection.toString()) {

http://jsfiddle.net/k61u66ek/10/

4
  • 1
    Thanks @t00thy, I've updated my answer to include a solution when type isn't available. Commented Aug 13, 2015 at 8:08
  • 1
    Or there is my solution, with catching the time on mouseclick http://jsfiddle.net/k61u66ek/15/ I set 200 milliseconds as difference between click and copy situation. Maybe it will help.
    – t00thy
    Commented Aug 13, 2015 at 8:25
  • it seems toString() is a better option than type=="Range", at least on IE11 & Firefox Commented May 23, 2017 at 7:57
  • The toString() method does not work for example when only images are selected. The type approach seems to have good browser support nowadays.
    – cdauth
    Commented Apr 5, 2022 at 15:23
14

Code snippet - cancels click event when text highlighted/selected:

document.getElementById('information').addEventListener('click', (e) => {
  const cellText = document.getSelection();
  if (cellText.type === 'Range') e.stopPropagation();
})

Demo link: http://jsfiddle.net/5472ja38/

Explanation

document.getSelection().type checks on the document object level if any of the text has been highlighted. If so, the type property is equal to 'Range' stop the event propagation which will cancel the click toggle button change.

Taken from MDN

A DOMString describing the type of the current selection. Possible values are:

None: No selection has currently been made.

Caret: The selection is collapsed (i.e. the caret is placed on some text, but no range has been selected).

Range: A range has been selected.

12

You could check if there is a selection made in the click event handler:

window.getSelection().toString();
4

You can use mouseup, mousedown and mousemove events to achieve this:

DEMO

var isDragging = false;
$("#clickshow")
.mousedown(function() {
    isDragging = false;
})
.mousemove(function() {
    isDragging = true;
 })
.mouseup(function() {
    var wasDragging = isDragging;
    isDragging = false;
    if (!wasDragging) {
        $("#information").toggle();
        $("#clicktoshow").toggle();
    }
});

SOURCE

3

Another way to go is to use the isCollapsed property of the selection. As per MDN, it returns true if the start and end of a selection are the same (which is true of a click):

document.getElementById('information').addEventListener('click', (e) => {
    if (!window.getSelection().isCollapsed) {
        return;
    }

    // do something here
});

0

You can check if the 'information' div is toggled :

function toggleInfo() {
    if(document.getElementById('information').style.display == 'none'){
         $("#clicktoshow").toggle();
         $("#information").toggle();
   } else { 
       // do nothing
   }
}

Check this Fiddle

0

In addition to the given answers, you'll want to set cursor:pointer👆, to indicate that the element is clickable. The simple way is to set on hover like so:

:hover {
  cursor: pointer;
}

However, since you also want to allow text highlighting, you'll want to bring back the text enter image description here cursor while highlighting is in progress. You'll need the active selector for that. Complete solution below

:hover:not(:active) {
  cursor: pointer;
}

This solution is not perfect, since it won't apply the text cursor if the element wasn't initially clicked on to start selection, but it's simple. Otherwise you need to listen to mouseup and mousedown events on a top-level object like window, and apply the styles conditionally with javascript.

Not the answer you're looking for? Browse other questions tagged or ask your own question.