I can't seem to figure this out. I have a div with some text in it. When the user selects pieces of it (totally at random, whatever they want), I want a small popup to occur with the text inside of it.
To initiative the popup, can I just do this? ...
$('#textdiv').click(function() {
But then how do I get only the selected/highlighted text?
jQuery isn't going to be of much use here, so you'll need pure JS to do the selection grabbing part (credit goes to this page):
function getSelected() {
if(window.getSelection) { return window.getSelection(); }
else if(document.getSelection) { return document.getSelection(); }
else {
var selection = document.selection && document.selection.createRange();
if(selection.text) { return selection.text; }
return false;
}
return false;
}
You were on the right track with the mouseup handler, so here's what I got working:
$('#test').mouseup(function() {
var selection = getSelected();
if (selection) {
alert(selection);
}
});
And a live demo: http://jsfiddle.net/PQbb7/7/.
Just updated first answer.
Try this
function getSelected() {
if(window.getSelection) { return window.getSelection(); }
else if(document.getSelection) { return document.getSelection(); }
else {
var selection = document.selection && document.selection.createRange();
if(selection.text) { return selection.text; }
return false;
}
return false;
}
/* create sniffer */
$(document).ready(function() {
$('#my-textarea').mouseup(function(event) {
var selection = getSelected();
selection = $.trim(selection);
if(selection != ''){
$("span.popup-tag").css("display","block");
$("span.popup-tag").css("top",event.clientY);
$("span.popup-tag").css("left",event.clientX);
$("span.popup-tag").text(selection);
}else{
$("span.popup-tag").css("display","none");
}
});
});
.popup-tag{
position:absolute;
display:none;
background-color:#785448d4;
color:white;
padding:10px;
font-size:20px;
font-weight:bold;
text-decoration:underline;
cursor:pointer;
-webkit-filter: drop-shadow(0 1px 10px rgba(113,158,206,0.8));
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Select any text :<br>
<textarea type="text" id="my-textarea" style="width:100%; height:200px;" >
While delivering a lecture at the Indian Institute of Management Shillong, Kalam collapsed and died from an apparent cardiac arrest on 27 July 2015, aged 83. Thousands including national-level dignitaries attended the funeral ceremony held in his hometown of Rameshwaram, where he was buried with full state honours.
</textarea>
<span class="popup-tag"></span>
see: https://jsfiddle.net/arunmaharana123/kxj9pm40/
We've just released an jQuery plugin called highlighter.js that should allow you to do this flexibly. The code is https://github.com/huffpostlabs/highlighter.js, feel free to ask any questions on the github page.
You can get it from the base DOM element likeso:
var start = $('#textdiv')[0].selectionStart;
var end = $('#textdiv')[0].selectionEnd;
var highlight = $('#textdiv').val().substring(start, end);
// Note the [0] part because we want the actual DOM element, not the jQuery object
At this point, you just need to bind it to a click event. I think in this case mouseup is the event you'd want to bind to, since a user clicks and holds the mouse and then releases it after they're done highlighting text.
The problem is this would not trigger users that use only the keyboard to highlight text. For that you'd want to use keyup on the element and filter for the right keystrokes.
You need a event listener that listen to mouseup event.
var bubbleDOM = document.createElement('div');
bubbleDOM.setAttribute('class', 'selection_bubble');
document.body.appendChild(bubbleDOM);
// Lets listen to mouseup DOM events.
document.addEventListener('mouseup', function (e) {
var selection = window.getSelection().toString();
if (selection.length > 0) {
renderBubble(selection);
}
}, false);
// Close the bubble when we click on the screen.
document.addEventListener('mousedown', function (e) {
bubbleDOM.style.visibility = 'hidden';
}, false);
// Move that bubble to the appropriate location.
function renderBubble(selection) {
bubbleDOM.innerHTML = selection;
bubbleDOM.style.visibility = 'visible';
}
Related
Here is a simple script function getSelectedText() that is working on button click. It means that when we select any text and click on the button, the function is creating a new NEWCLASS div successfully. But now I want to use a shortcut key, like CTRL+W to get selected text in the NEWCLASS div.
I tried this code but it doesn't work. Please, check it and let me know what mistake I am making here.
var input = document.getElementsByTagName("body")[0];
input.addEventListener("keyup", function(event) {
if (event.keyCode === 37) {
event.preventDefault();
document.getElementById("myBtn").click();
}
});
My code:
// Function to get the Selected Text
function getSelectedText() {
var selectedText = '';
// #### create a new element with variable (nw) #### //
var nw = document.createElement("div"); // Element's tag
nw.className = "NEWCLASS"; // Element's class name
// some applied style
// window.getSelection
if (window.getSelection) {
selectedText = window.getSelection();
}
// document.getSelection
else if (document.getSelection) {
selectedText = document.getSelection();
}
// document.selection
else if (document.selection) {
selectedText =
document.selection.createRange().text;
} else return;
// #### get the Selected text appended to body #### //
nw.innerHTML = selectedText;
document.getElementsByClassName('maintitle')[0].prepend(nw); // Append element to body
}
<button id="mybtn" onclick="getSelectedText()">Button</button>
<p>Select any part of this sentence and press the button. Select any part of this sentence and press the button. Select any part of this sentence and press the button</p>
<div class="maintitle"> </div>
the ctrl key with w will close the chrome browser so I used "Z" key for that you can replace the key code with whatever you want.
Find the keycodes here
const keySelected = new Set();
document.addEventListener('keydown', (e) => {
keySelected.add(e.which);
if(keySelected.has(17) && keySelected.has(90)){
getSelectedText();
}
});
document.addEventListener('keyup', (e) => {
keySelected.delete(e.which);
});
/*
//jquery code if anyone want
$(document).ready(function(){
const keySelected = new Set();
$(document).keydown(function (e) {
keySelected.add(e.which);
if(keySelected.has(17) && keySelected.has(90)){
getSelectedText()
}
});
$(document).keyup(function (e) {
keySelected.delete(e.which);
});
});
*/
// Function to get the Selected Text
function getSelectedText() {
var selectedText = '';
// #### create a new element with variable (nw) #### //
var nw = document.createElement("div"); // Element's tag
nw.className = "NEWCLASS"; // Element's class name
// some applied style
// window.getSelection
if (window.getSelection) {
selectedText = window.getSelection();
}
// document.getSelection
else if (document.getSelection) {
selectedText = document.getSelection();
}
// document.selection
else if (document.selection) {
selectedText =
document.selection.createRange().text;
} else return;
// #### get the Selected text appended to body #### //
nw.innerHTML = selectedText;
document.getElementsByClassName('maintitle')[0].prepend(nw); // Append element to body
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="mybtn" onclick="getSelectedText()">Button</button>
<p>
Select any part of this sentence
and press the button. Select any part of this sentence
and press the button. Select any part of this sentence
and press the button
</p>
<div class="maintitle"> </div>
You don't need to recall the event on the button, you should be capable of detect the press of the key you want and call the function, like:
document.addEventListener('keydown', (e) => {
if (e.ctrlKey && e.code === 'keyW') {
getSelectedText();
}
});
Ctrl+W is not such a good choice, as on Windows it is a common shortcut key for closing the current Window or document, so your script will not even get the key event.
But I'll pick Ctrl+Y instead.
You should not use the deprecated keyCode property -- nor the which property which another answer suggests -- notice the red notice in the MDN documentation.
It is even easier with the key property. And you can use the ctrlKey property to know whether the control key is down.
I would use the keydown instead of the keyup event, as you can then cancel any default effect of the key, with a call to cancelDefault.
document.addEventListener("keydown", (event) => {
if (event.ctrlKey && event.key == "y") {
event.preventDefault();
getSelectedText();
}
});
// Your original code:
function getSelectedText() {
var selectedText = '';
var nw = document.createElement("div"); // Element's tag
nw.className = "NEWCLASS"; // Element's class name
if (window.getSelection) {
selectedText = window.getSelection();
} else if (document.getSelection) {
selectedText = document.getSelection();
} else if (document.selection) {
selectedText = document.selection.createRange().text;
} else return;
nw.innerHTML = selectedText;
document.getElementsByClassName('maintitle')[0].prepend(nw);
}
<button id="mybtn" onclick="getSelectedText()">Button</button>
<p>Select any part of this sentence and press the button or enter Ctrl+Y.
Select any part of this sentence and press the button or enter Ctrl+Y.
Select any part of this sentence and press the button or enter Ctrl+Y.
</p>
<div class="maintitle"> </div>
This should work
var input = document.getElementsByTagName("body")[0];
input.keypress("w", function(event) {
if (event.ctrlKey) {
event.preventDefault();
document.getElementById("myBtn").click();
}
});
I am faced with following: when I try to select text in a contenteditable element and the end of the selection is the start of the element content, then no select event is fired and there are no Selection and Range objects.
Could somebody please give me any advice on why this might occur or how I can prevent this?
Code responsible for getting selection range:
$('div[contenteditable="true"]').bind("mouseup keyup touchend", function() {
lastCaretIndex = getSelectionRange();
});
function getSelectionRange() {
var sel;
if (window.getSelection) {
sel = window.getSelection();
console.log(sel); // this doesn't print anything event empty string
if (sel.rangeCount) {
return sel.getRangeAt(0);
}
} else if (document.selection) {
return document.createRange();
}
return null;
}
<div id="main-input" contenteditable="true">Hello world!</div>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
JSFiddle (open your browser console to make sure that selection doesn't get logged).
The issue is that you only log selection changes when specific events occur on the contenteditable element. More specifically, you have
$('div[contenteditable="true"]').bind("mouseup keyup touchend", // ...
In particular the mouseup event will normally be triggered when the selection changes. Except when it doesn't. When you release the mouse outside of the editable div (which you do in your example!), then the div will never receive a mouseup event and thus never log the selection.
There are two ways around this:
Listen for events on the entire body. Downsides are that you receive more events that do not influence the selection and that it is still possible to get mouseup events outside of the page.
Listen for the selectionchange event.
document.addEventListener('selectionchange', function(event) {
console.log(event.type);
});
<div contenteditable="true">Hello world!</div>
You can of course still access the selection as you currently do inside this event handler. This event is triggered every time the selection changes, so you may want to throttle it.
Full implementation of that can be found below.
function handler() {
// do whatever you want here
// this shows the selection and all ranges it consists of
var sel = window.getSelection(),
ranges = Array(sel.rangeCount).fill(0).map((_, i) => sel.getRangeAt(i));
ranges = ranges.map((r) => `${r.startOffset}-${r.endOffset}`).join(';');
console.log(`Selection [${ranges}:"${sel.toString()}"]`);
}
function throttle(func) {
var timeoutId = false,
called = false,
wrap = function() {
if (!called) {
clearInterval(timeoutId);
timeoutId = false;
} else {
func();
}
called = false;
};
return function() {
if (timeoutId === false) {
func();
timeoutId = setInterval(wrap, 500);
} else {
called = true;
}
};
}
document.addEventListener('selectionchange', throttle(handler));
<div contenteditable="true">Hello world!</div>
Your actual code works perfectly and logs a Selection object in the console, even if the end of the selection is the start of the element content.
Indeed you need to log this Selection text instead of logging the whole object which reflects the whole Selection object changes for each event.
I updated your snippet to log the text of the selection using Selection.toString(), you can see it working here:
$('div[contenteditable="true"]').bind("mouseup keyup touchend", function() {
lastCaretIndex = getSelectionRange();
});
function getSelectionRange() {
var sel;
if (window.getSelection) {
sel = window.getSelection();
console.log(sel.toString()); // this doesn't print anything event empty string
if (sel.rangeCount) {
return sel.getRangeAt(0);
}
} else if (document.selection) {
return document.createRange();
}
return null;
}
<div id="main-input" contenteditable="true">Hello world!</div>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
You can check this answer, it shows and explains the perfect way to get text selection.
We have the following Javascript code:
function makeEditableAndHighlight(colour) {
sel = window.getSelection();
if (sel.rangeCount && sel.getRangeAt) {
range = sel.getRangeAt(0);
document.designMode = "on";
sel.removeAllRanges();
sel.addRange(range);
}
if (!document.execCommand("HiliteColor", false, colour)) {
document.execCommand("BackColor", false, colour);
}
document.designMode = "off";
}
// This is the highlighting function. It takes a color as an argument.
function highlight(colour) {
var range, sel;
if (window.getSelection) {
try {
if (!document.execCommand("BackColor", false, colour)) {
makeEditableAndHighlight(colour);
window.getSelection().removeAllRanges();
}
} catch (ex) {
makeEditableAndHighlight(colour)
}
} else if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
range.execCommand("BackColor", false, colour);
}
}
// This returns the highlight to transparent (no highlighting) when the user clicks away in the document.
function body() {
document.getElementsByTagName("body")[0].addEventListener(
"click",
function(event){
highlight('transparent');
}
);
}
It works like this:
When there's text selected in a page, if you press a button (say an extension popup) then that text gets a yellow highlight and is automatically deselected.
That's where the problem comes in.
Because the text is automatically deselected, I cannot revert the highlight back to the same text (since there's no selection anymore)
So the focus of this question is on the last part, namely:
function body() {
document.getElementsByTagName("body")[0].addEventListener(
"click",
function(event){
highlight('transparent');
}
);
}
This only works if I select the same text and click anywhere in the document.
How I would like it to work is to be able to click anywhere in the document, and any highlighted text to be set back to original (or transparent if original not possible).
I'm aiming for any text because I have no selection here.
So basically this function should work like this:
if selection present and button pressed > highlight the selection and automatically remove all selections.
when a click is registered anywhere in the page, remove any highlighting on any text, regardless of whether there's a selection or not.
Again, this code seems to work very good up until the last part, the 'removing any highlight present' part.
You can replace your call to highlight from body to have an extra parameter prevRange which is a global one. and then reset prevRange to be undefined after the call.
The signature of highlight can be
function highlight(colour, oldRange) {
and start like
function highlight(colour, oldRange) {
var range, sel, oldSelection;
if (typeof(oldRange) !== 'undefined') {
oldSelection = window.getSelection();
oldSelection.removeAllRanges();
oldSelection.addRange(oldRange);
}
sel = oldSelection || window.getSelection();
if (sel) {
The rest of the function can work with sel.
The old range will need to be stored in prevRange just before the deselection, and makeEditableAndHighlight can also get the optional parameter oldRange (and have a test similar to the one after the "function highlight(colour, oldRange) {" signature)
I have working code that inserts <br> when you hit enter in a content editable div. (Browsers have various defaults of inserting <div> or <p> instead)
The problem is that it kills the default behavior of hitting enter to add another list item when building ordered or unordered lists. So my question is, can you detect if the text insertion point is within a list item, and if so, disable the javascript that deals with the enter key?
Working code: http://jsfiddle.net/kthornbloom/RCdhS/
You need to do some DOM tree checking on the node containing the selection. Here's a demo that will work in all major browsers:
http://jsfiddle.net/CeMxs/2/
Code:
function isSelectionInsideElement(tagName) {
var sel, containerNode;
tagName = tagName.toUpperCase();
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount > 0) {
containerNode = sel.getRangeAt(0).commonAncestorContainer;
}
} else if ( (sel = document.selection) && sel.type != "Control" ) {
containerNode = sel.createRange().parentElement();
}
while (containerNode) {
if (containerNode.nodeType == 1 && containerNode.tagName == tagName) {
return true;
}
containerNode = containerNode.parentNode;
}
return false;
}
http://jsfiddle.net/RCdhS/2/
.on('keypress', 'document', function (e) {
if (!$('li').focus();) {
...
}
}
});
Jsfiddle: http://jsfiddle.net/qTEmc/1/
I need to associate events with the keypress event on links which are added in the contenteditable.
If you try typing in the contenteditable area in the linked jfiddle, you'll see it creates a link and you can type within it. I fyou press return, you go to a newline. What I want is for pressing return in the new link to trigger a function. For the sake of progress, I'm just trying to get it to return an alert at the moment.
Does anyone know a reliable way to do this?
You won't be able to detect key events within the links themselves because they don't fire key events. Instead, you'll need to adapt your existing keypress handler for the contenteditable element to inspect the selection to see if it lies within a link. Here's a function to do that. I've also updated your demo.
function selectionInsideLink() {
var node = null, sel;
// Get the selection container node
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount) {
node = sel.getRangeAt(0).commonAncestorContainer;
}
} else if (document.selection) {
sel = document.selection;
if (sel.type != "Control") {
node = sel.createRange().parentElement();
}
}
// Check if the node is or is contained inside a link
while (node) {
if (node.nodeType == 1 && node.tagName.toLowerCase() == "a") {
return true;
}
node = node.parentNode;
}
return false;
}