Click to select all text in div not working on mobile - javascript

I want to select all text inside a div via a single click/tap. See example code (jsFiddle):
document.getElementById('target').addEventListener('click', function (event) {
setTimeout(function() {
window.getSelection().selectAllChildren(
document.getElementById('target')
)
}, 100);
});
<div id="target">
<span>click to select all </span>
<span>text</span>
</div>
It works on desktop, but not properly on iOS using Safari (likely same problem on Android as well).
As you can see, I added a setTimeout() in an attempt to capture the mobile tap, but it only works after taping many times or focusing on the div via a long tap.
How can I make it select the full text with a single tap on iOS using pure JS?
Edit 1: Setting touchstart as event trigger might work, but avoiding it as it can be triggered by scrolling down the page and accidentally touching the element.
Edit 2: Making a bounty for a tested solution which works on iOS and mobile in general.

Instead of JS you could use a CSS only approach with user-select: all; (MDN)
#target {
/*
-webkit-touch-callout: all; /* iOS Safari /
-webkit-user-select: all; /* Safari /
-khtml-user-select: all; /* Konqueror HTML /
-moz-user-select: all; /* Firefox /
-ms-user-select: all; /* Internet Explorer/Edge /
*/
user-select: all;
}
<div id="target">
<span>click to select all </span>
<span>text</span>
</div>
Be aware that if you want users to be able to select a custom range within your #target it won't let them do that with this approach as it will "force" an all-text selection. But if that is not an issue/requirement the CSS approach is more elegant (IMHO) and should work cross device/platform (if you need to support really old browsers, you can use vendor prefixes as well).

I actually like #exside's solution, but cross platform support is sadly poor.
Anyways, the solution.
We'll use the window.getSelection api which returns a Selection object and move on from there.
const selectableTarget = document.getElementById('target');
selectableTarget.onclick = () => {
// Get reference to the Selection object and remove all selected ranges
const selection = window.getSelection();
selection.removeAllRanges();
// Create a new range and select the target node
const range = document.createRange();
range.selectNode(selectableTarget);
// Add the range to the selection
selection.addRange(range);
};
<div id="target">
<span>Select me please</span>
</div>

You can try window selection APIs or document.selection API for old browsers: -
document.getElementById('target').addEventListener('click', function (event) {
if (window.getSelection && document.createRange) { //Browser compatibility
sel = window.getSelection();
if(sel.toString() == ''){ //no text selection
window.setTimeout(function(){
range = document.createRange(); //range object
range.selectNodeContents(event.target); //sets Range
sel.removeAllRanges(); //remove all ranges from selection
sel.addRange(range);//add Range to a Selection.
},100);
}
}else if (document.selection) { //older ie
sel = document.selection.createRange();
if(sel.text == ''){ //no text selection
range = document.body.createTextRange();//Creates TextRange object
range.moveToElementText(el);//sets Range
range.select(); //make selection.
}
}
});
jsfiddle: https://jsfiddle.net/u4yhn3sk/41/

Related

getRangeAt(0) bug in MS Edge, workaround?

I'm working on a project where users can highlight text in a PDF file, and then use getSelection(), getRangeAt(), and getClientRects() to save the boundaries of the highlight for future reference. The problem I'm running into is Microsoft Edge appears to have a bug when executing getRangeAt(0) if the selection crosses elements with position:absolute. See below fiddle and view console in both Edge (broken) and Chrome/Firefox (working as expected). After running in Edge, remove the position:absolute style and run again, Edge now correctly returns the expected rects.
http://jsfiddle.net/Lbgce2km/2/
document.addEventListener('mouseup', function(e) {
let sel = window.getSelection();
let range = sel.getRangeAt(0);
let rects = range.getClientRects();
//check console for result.
console.log('Rects: ', rects);
});
div {
position:absolute
}
<div style='margin-top: 10px'>Microsoft Edge is the default web browser on Windows 10, Windows 10 Mobile, and Xbox One consoles, replacing Internet Explorer 11 and Internet Explorer Mobile. engine</div>
<div style='margin-top: 75px' >
Microsoft initially announced that Edge would support the legacy Trident (MSHTML) layout engine for backwards compatibility, but later said that, due to "strong feedback", Edge would use a new engine, while Internet Explorer would continue to provide the legacy
</div>
<div style='margin-top: 175px'>
The browser includes an integrated Adobe Flash Player (with an internal whitelist allowing Flash applets on Facebook websites to load automatically, bypassing all other security controls requiring user activation)[15] and a PDF reader. It also supports asm.js.
</div>
I've tried working around problem by creating an additional range, setting the position equal to that of the selected range, then extracting the contents of the selected range and insert that inside of a span into the additional range, then returning the rects of the span but it's of course out of place.
const sel = window.getSelection();
const range = sel.getRangeAt(0)
let addRange = document.createRange();
addRange.setStart(sel.anchorNode, sel.anchorOffset);
addRange.setEnd(sel.focusNode, sel.focusOffset);
let span = document.createElement("span");
let con = range.extractContents();
span.appendChild(con);
addRange.insertNode(span);
Anyone have any suggestions on a possible workaround?

How to scroll to automatically scroll to the bottom of an editable div onload

How can I get an editable div to remember the position of the scroll bar? Im currently programming a Chrome extension and have a notepad style editable div which users can write notes in. I have the code listed below currently which autofocuses the div onload and displays the caret at the end of the text within the div.
I now just need my scroll bar to scroll level with the caret if its further down the div.
var div = document.getElementById("edit_notepad");
div.onfocus = function() {
window.setTimeout(function() {
var sel, range;
if (window.getSelection && document.createRange) {
range = document.createRange();
range.selectNodeContents(div);
range.collapse(false);
sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (document.body.createTextRange) {
range = document.body.createTextRange();
range.moveToElementText(div);
range.collapse(false);
range.select();
}
}, 1);
};
div.focus();
You may want to try the different approaches done in Scroll to bottom of Div on page load (jQuery) and choose which best suits your concern.
One suggested solution is to scroll down to the height of the div as suggested by #Mike Todd
Correct version:
$('#div1').scrollTop($('#div1')[0].scrollHeight);
This related SO post might also help.
Why don't you try some simple localStorage?
var position = localStorage.position || 0;
div.scrollTop = position;
localStorage.setItem("position", div.scrollTop);
The code above should get and set the position of the div scroll amount, as far as I know.

Strange blue border on Firefox

Please, take a look at this code
http://www.jsfiddle.net/tt13/5CxPr/21
On Firefox it shows strange blue border when you select multiple row by pressing ctrl button but on Chrome it doesn't.
Using latest Firefox 10.0.2.
Is that browser related bug?
This is due to text being selected - native browser behavior.
You can observe the same issue in Chrome as well by using the SHIFT key instead of CTRL.
To overcome this, you can simply clear the selection right after user click the cell to select:
$(".subject").live('click',function(event) {
if(event.ctrlKey) {
$(this).toggleClass('selected');
} else {
$(".subject").removeClass("selected");
$(this).addClass("selected");
}
if (document.selection)
document.selection.empty();
else if (window.getSelection)
window.getSelection().removeAllRanges();
});
Updated fiddle.
Try setting CSS property -moz-user-select to the table to disable the default selection behavior:
table { -moz-user-select: none; }
MDN
This works for current version of Firefox 20.0.1 if you're prepared to add an extra element inside your cell to allow the text to still be selectable.
td { -moz-user-select: -moz-none }
td * { -moz-user-select: text }
http://jsfiddle.net/nukj7/

Is possible to exactly find position of KeyPress event in browsers except IE?

From my recent question, I use KeyPress event for detecting '#' character in rich text editor. By the way, I just found that KeyPress event in other browser like Firefox 3.5 and Google Chrome 4 do not return any position in this event.
For clarify, my position is a distance from top or left of screen. In this case, it should be distance between new character and IFrame screen. In IE, I can find it in event object like x, y, offsetX, offsetY.
Is possible to find position from last character that was typed? Or you have any other idea for finding it.
For an input/textarea, you can find out the current position of the cursor using input.selectionStart/selectionEnd. If it was a simple insertion keypress that will be just ahead of the new character. (Don't rely on it, there are browsers that support neither the IE nor the Mozilla extensions.)
If your ‘rich text editor’ is a HTML designMode/contentEditable thing (the horror!) then I would guess you'd have to use window.getSelection() to read the position of the cursor. This is even worse:
if (window.getSelection) {
var selection= window.getSelection();
if (selection) {
if (selection.getRangeAt) { // Mozilla
if (selection.rangeCount>=1) {
var range= selection.getRangeAt(0);
return [range.startContainer, range.startOffset];
}
} else if (selection.focusNode) { // Webkit
return [selection.focusNode, selection.focusOffset];
}
}
}
Seems to work with designMode for Mozilla, haven't tested in the others.

How do I get the (x, y) pixel coordinates of the caret in text boxes?

I am using jQuery and trying to find a cross browser way to get the pixel coordinates of the caret in <textarea>s and input boxes such that I can place an absolutely positioned div around this location.
Is there some jQuery plugin? Or JavaScript snippet to do just that?
I've looked for a textarea caret coordinates plugin for meteor-autocomplete, so I've evaluated all the 8 plugins on GitHub. The winner is, by far, textarea-caret-position from Component.
Features
pixel precision
no dependencies whatsoever
browser compatibility: Chrome, Safari, Firefox (despite two bugs it has), IE9+; may work but not tested in Opera, IE8 or older
supports any font family and size, as well as text-transforms
the text area can have arbitrary padding or borders
not confused by horizontal or vertical scrollbars in the textarea
supports hard returns, tabs (except on IE) and consecutive spaces in the text
correct position on lines longer than the columns in the text area
no "ghost" position in the empty space at the end of a line when wrapping long words
Here's a demo - http://jsfiddle.net/dandv/aFPA7/
How it works
A mirror <div> is created off-screen and styled exactly like the <textarea>. Then, the text of the textarea up to the caret is copied into the div and a <span> is inserted right after it. Then, the text content of the span is set to the remainder of the text in the textarea, in order to faithfully reproduce the wrapping in the faux div.
This is the only method guaranteed to handle all the edge cases pertaining to wrapping long lines. It's also used by GitHub to determine the position of its # user dropdown.
Note: this answer describes how to get the character co-ordinates of the text-cursor/caret. To find the pixel-co-ordinates, you'll need to extend this further.
The first thing to remember is that the cursor can be in three states
a regular insertion cursor at a specific position
a text selection that has a certain bounded area
not active: textarea does not have focus. Has not been used.
The IE model uses the Object document.selection, from this we can get a TextRange object which gives us access to the selection and thus the cursor position(s).
The FF model/Opera uses the handy variables [input].selectionStart and selectionEnd.
Both models represent a regular ative cursor as a zero-width selection, with the left bound being the cursor position.
If the input field does not have focus, you may find that neither is set.
I have had good success with the following code to insert a piece of text at the current cursor location, also replacing the current selection, if present.
Depending on the exact browser, YMMV.
function insertAtCursor(myField, myValue) {
/* selecion model - ie */
if (document.selection) {
myField.focus();
sel = document.selection.createRange();
sel.text = myValue;
}
/* field.selectionstart/end firefox */
else if (myField.selectionStart || myField.selectionStart == '0' ) {
var startPos = myField.selectionStart;
var endPos = myField.selectionEnd;
myField.value = myField.value.substring(0, startPos)
+ myValue
+ myField.value.substring(endPos, myField.value.length);
myField.selectionStart = startPos + myValue.length;
myField.selectionEnd = startPos + myValue.length;
myField.focus();
}
// cursor not active/present
else {
myField.value += myValue;
}
Bug Note: links are not being correctly marked up in the top para.
Selection object: http://msdn.microsoft.com/en-us/library/ms535869(VS.85).aspx
TextRange object: http://msdn.microsoft.com/en-us/library/ms535872(VS.85).aspx

Categories