We have a HTML page. Is it possible to select (by mouse) few words of a paragraph, get reference to those selected words and encapsulate them, say, by the <span>...</span> tag programatically? We can use jQuery or HTML5/CSS3?
You can use a mouseup handler and use getSelection. Say you have a div called testtagging, then this is a way to add a span to a selected text within that div. See this jsfiddle.
$('#testtagging').on('mouseup',tag);
function tag(e){
tagSelection('<span style="color:red">$1</span>');
}
function tagSelection(html) {
var range, node;
if (document.selection && document.selection.createRange) {
//IE
range = document.selection.createRange();
range.pasteHTML(html.replace(/\$1/,range.text));
return true;
}
if (window.getSelection && window.getSelection().getRangeAt) {
//other browsers
range = window.getSelection().getRangeAt(0);
node = range.createContextualFragment(
html.replace(/\$1/,range.toString())
);
range.deleteContents();
range.insertNode(node);
}
return true;
}
[edit] adjusted for use with IE too. JsFiddle is also adapted
JSFiddle working example
Propose to wrap all paragraph words into span elements:
var r = /(\S+)/ig;
var text = $("p").text();
$("p").html(text.replace(r, "<span class='w'>$1</span>"));
Then bind hover/click events:
$("p > .w").on("hover", function()
{
$(this).toggleClass("hover");
})
.on("click", function()
{
$(this).toggleClass("selected");
});
If you want to parse words from selected text range, you should use window.getSelection().
Refer to this question or ask me to adapt this code.
Related
I have a div tag that I make editable.
I do not want any HTML in that tag so I do not let users enter any. However, when the user does a copy / paste, it is not unlikely to include tags.
I have some jQuery code to capture the paste event and just in case I tried using my saveSelection() and restoreSelection() functions which I show below which work find in many situations but here they fail...
Fiddle: http://jsfiddle.net/9wm0oeah/2/
jQuery("#that_div").on("paste", function()
{
setTimeout(function()
{
// remove any HTML
var selection = saveSelection();
jQuery("#that_div").text(jQuery("#that_div").text());
restoreSelection(selection);
}, 0);
});
function saveSelection()
{
var sel;
if(document.selection)
{
return document.selection.createRange();
}
else
{
sel = window.getSelection();
if(sel.getRangeAt && sel.rangeCount > 0)
{
return sel.getRangeAt(0);
}
else
{
return null;
}
}
//NOTREACHED
}
function restoreSelection(range)
{
var sel;
if(document.selection)
{
range.select();
}
else
{
sel = window.getSelection();
sel.removeAllRanges();
sel.addRange((range));
}
}
Do you have any idea why it fails?
When you change the DOM nodes inside your editable div, any range exists within or partially within that div has to change to accommodate the changes. If you completely replace the content, as your code does, the nodes that the range's boundaries were relative to are destroyed and the range has to revert to a default state.
You could use a character offset-based solution instead. For example: Can't restore selection after HTML modify, even if it's the same HTML
I would like to select text automatically (I mean something with the Ranges) in some div to in order to unit test my script in JS.
My problem is that I want to be able to select text in different ways here are some examples of selection (selection represented by [ for start and ] for end)
<div>[ here I select all the text in the div ]</div
<div>This is just a <b>piece [ of</b> selection]</div>
<div><ul><li>Also with [list</li><li>And why not select many items ?</li></ul><p>With a p ?] but not the complete p !</p>
I want to do that because the user can do that and so I should test every cases of use. (If I don't, the unit test is quite useless...)
Do you have an idea about how to select what I want ? (if possible...)
I found this on the web.
This a function that when called on a particular HTML element will auto-select all of its inner text. The function works on elements such as INPUT (text), TEXTAREA, DIV, SPAN, TD, and PRE. Cross browser compatible.
var autoSelect = function(event) {
var event = event || window.event,
elem = event.target || event.srcElement,
tag = (elem.tagName || "").toLowerCase(),
sel, range;
if (tag === "textarea" || tag === "input") {
try {
elem.select();
} catch(e) {
// May be disabled or not selectable
}
} else if (window.getSelection) { // Not IE
sel = window.getSelection();
range = document.createRange();
range.selectNodeContents(elem);
sel.removeAllRanges();
sel.addRange(range);
} else if (document.selection) { // IE
document.selection.empty();
range = document.body.createTextRange();
range.moveToElementText(elem);
range.select();
}
};
To attach this function to an element:
document.getElementById("foobar").onclick = autoSelect;
You can do it using a regex:
var divStr = document.getElementById('yourDiv').innerHTML;
var matchs = /\[(.*?)\]/.exec(divStr);
console.log(matchs[1]);
We get the inner html string, then parses everything is between [ ], and finnaly get the grouped content.
I have a div (#recommendTextArea) which is editable, in which that I try to modify the innerHTML of this div when a user clicks on a list (this is called .display_box), the function looks like this. Basically it appends a span to the innerHTML of the div and then it hides the friendList, upon hiding it also tries to restoreTheSelection and before appending the extra span I called saveSelection.
$(".display_box").live("click",function()
{
selRange = saveSelection();
console.log(selRange);
var username = $(this).attr('title');
var old = $("#recommendTextArea").html();
var content = old.replace(word, " "); //replacing #abc to (" ") space
var E ="<span contenteditable='false'>"+ username + "</span> ";
content = [content.slice(0, start), E, content.slice(start)].join('');
$("#recommendTextArea").html(content);
$("#friendsList").hide(function(){
restoreSelection(selRange);
});
});
I have the following function to restore and save selection:
function saveSelection() {
if (window.getSelection) {
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
return sel.getRangeAt(0);
}
} else if (document.selection && document.selection.createRange) {
return document.selection.createRange();
}
return null;
}
function restoreSelection(range) {
if (range) {
if (window.getSelection) {
sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (document.selection && range.select) {
range.select();
}
}
}
However this doesn't work as expected, the cursor is no where to be seen when I click on an item. What am I doing wrong here?
You have a few issues:
1) Timing: the "click" event is way too late to grab selection (ALWAYS debug this, it's super easy to see the editable DIV has long lost focus and selection by that time). Use "mousedown" instead.
2) You can't store selection range like this - changing the selection context (in your case the innerHTML of the commonAncestorContainer) will wipe that range (for some reason even cloned range objects get wiped for me). If you manage to get a copy (via jQuery.extend for example) it will become invalid because the text node inside is not guaranteed to remain the same. My best guess is to go with storing start/end offset and if needed the related nodes as required by the range. Restore the range properties after the HTML is modified.
3) As with 1) focus is crucial to maintain selection, so that click on the list.. make sure you prevent the default before exiting the handler so focus and you new selection will remain in the DIV.
Can't figure out the exact use case from your code but this is my test sample and you can adjust from here as needed: http://jsfiddle.net/damyanpetev/KWDf6/
I have built an editor that converts markdown to html. Right now I have to use jquery autosize plugin to resize the text area as it grows.
If I use a content-editable div I can bypass it. But the problem with content editable div is that it does not preserve new lines. It inserts a new div every time return key is pressed. This breaks the rendering of markdown to html for my application.
Is there any way I can make a content editable div behave exactly like text area?
After searching for an answer and not finding anything that worked completely I wrote my own jQuery plugin.
https://github.com/UziTech/jquery.toTextarea.js
I used white-space: pre-wrap; and inserted '\n' on enter. That way I can use $("div").text() to get the text and not worry about removing tags and formatting <br/>'s
DEMO:
http://jsfiddle.net/UziTech/4msdgjox/
Edit
After the #Mr_Green comment above, you should have a look at Make a <br> instead of <div></div> by pressing Enter on a contenteditable
The JS code to make it right is :
$(function(){
$("#editable")
// make sure br is always the lastChild of contenteditable
.live("keyup mouseup", function(){
if (!this.lastChild || this.lastChild.nodeName.toLowerCase() != "br") {
this.appendChild(document.createChild("br"));
}
})
// use br instead of div div
.live("keypress", function(e){
if (e.which == 13) {
if (window.getSelection) {
var selection = window.getSelection(),
range = selection.getRangeAt(0),
br = document.createElement("br");
range.deleteContents();
range.insertNode(br);
range.setStartAfter(br);
range.setEndAfter(br);
range.collapse(false);
selection.removeAllRanges();
selection.addRange(range);
return false;
}
}
});
})
;
You can intercept the Enter key press and replace it with a <br> with Javascript :
$(function(){
$("#editable").keypress(function(e) {
if (e.which == 13) {
e.preventDefault();
if (document.selection) {
document.selection.createRange().pasteHTML("<br/>");
} else {
$(this).append("<br/>");
}
}
});
});
I have the following div:
<div id="query" style="width:500px; height:200px;border:1px solid black"
spellcheck="false" contenteditable="true"></div>
where Clients can write their SQL queries. What I was trying to do is wrap words the client enters right after hitting Space with a span and give this span a certain class according to the word typed:
example
If the client types select i need to wrap this select word like this in the div:
<span class='select'> SELECT </span> <span> emp_name </span>
CSS
.select{color:blue ;text-transform:uppercase;}
It is something very similar to what jsFiddle does. How can i achieve this?
Here is what i have tried so far : jsFiddle
$(function(){
$('div').focus() ;
$('div').keyup(function(e){
//console.log(e.keyCode) ;
if(e.keyCode == 32){
var txt = $('div').text() ;
var x = 'SELECT' ;
$('div:contains("'+x+'")').wrap("<span style='color:blue ;
text-transform:uppercase;'>") ;
if(txt == 'SELECT'){
console.log('found') ; // why This Doesn't do any thing ?
}
}
});
});
I did a proof of concept with some modifications from what you originally had. See below,
DEMO: http://jsfiddle.net/cgy69/
$(function() {
$('div').focus();
var x = ['SELECT', 'WHERE', 'FROM'];
$('div').keyup(function(e) {
//console.log(e.keyCode) ;
if (e.keyCode == 32) {
//using .text() remove prev span inserts
var text = $.trim($(this).text()).split(' ');
$.each(text, function(i, v) {
$.each(x, function(j, xv) {
if (v.toUpperCase() === xv) {
text[i] = '<span style="color: blue; text-transform: uppercase;">' + v + '</span>';
}
});
});
$(this).html(text.join(' ') + ' ');
setEndOfContenteditable(this);
}
});
function setEndOfContenteditable(contentEditableElement) {
var range, selection;
if (document.createRange) //Firefox, Chrome, Opera, Safari, IE 9+
{
range = document.createRange(); //Create a range (a range is a like the selection but invisible)
range.selectNodeContents(contentEditableElement); //Select the entire contents of the element with the range
range.collapse(false); //collapse the range to the end point. false means collapse to end rather than the start
selection = window.getSelection(); //get the selection object (allows you to change selection)
selection.removeAllRanges(); //remove any selections already made
selection.addRange(range); //make the range you have just created the visible selection
}
else if (document.selection) //IE 8 and lower
{
range = document.body.createTextRange(); //Create a range (a range is a like the selection but invisible)
range.moveToElementText(contentEditableElement); //Select the entire contents of the element with the range
range.collapse(false); //collapse the range to the end point. false means collapse to end rather than the start
range.select(); //Select the range (make it the visible selection
}
}
});
You going to extend this further to handle
Backspace
HTML contents from previous inserts
Cursor position Partially done, editing in the middle would still mess up the caret.
and more..
Starting with a contenteditable element we can replace the markup as we need by operating directly on its innerHtml:
$('#query-container').on('keyup', function(e){
var $this = $(this);
//(?!\<\/b\>) negative lookahead is used so that anything already wrapped
//into a markup tag would not get wrapped again
$this.html($this.html().replace(/(SELECT|UPDATE|DELETE)(?!\<\/b\>)/gi, '<b>$1</b>'));
setEndOfContenteditable(this);
});
IMO this is a more readable option. Add the rangeselect method from the previous answer and we have a working fiddle