How to select the text of a span on click? - javascript

I am looking for a way to select the text inside a span using jquery when the text is clicked on.
For example in the html snippet below, I want the text "\apples\oranges\pears" to become selected when it is clicked on.
<p>Fruit <span class="unc_path">\\apples\oranges\pears</span></p>
I've tried implementing this myself to no avail.

It could be implemented with native JavaScript. A working demonstration on jsFiddle. Your code could be like this:
$('.unc_path').click(function (){
var range, selection;
if (window.getSelection && document.createRange) {
selection = window.getSelection();
range = document.createRange();
range.selectNodeContents(this);
selection.removeAllRanges();
selection.addRange(range);
} else if (document.selection && document.body.createTextRange) {
range = document.body.createTextRange();
range.moveToElementText(this);
range.select();
}
});

You can use CSS to do this more easily than JS with style="user-select: all;"
add cursor: pointer; so its obvious they can click...
See code snippet:
<p>
Fruit
<span style="user-select: all; cursor: pointer;">\\apples\oranges\pears</span>
</p>

A working demonstration : http://jsfiddle.net/dystroy/V97DJ/
$('.unc_path').click(function (){
var text = $(this).text();
var $input = $('<input type=text>');
$input.prop('value', text);
$input.insertAfter($(this));
$input.focus();
$input.select();
$(this).hide();
});​
The idea (see comment above) is to dynamically replace the span with an input, only cross-browser way I know to have selected text.
Note that this is only half the road, as you probably want to deselect, style to remove border, etc.
And I must also precise that an input, contrary to a span, cannot span on multiple lines.
I don't think this could/should be used in a real application except in a very specific point.
EDIT : new version : http://jsfiddle.net/dystroy/A5ZEZ/
In this version the text comes back to normal when focus is lost.
$('.unc_path').click(function (){
var text = $(this).text();
var $this = $(this);
var $input = $('<input type=text>');
$input.prop('value', text);
$input.insertAfter($(this));
$input.focus();
$input.select();
$this.hide();
$input.focusout(function(){
$this.show();
$input.remove();
});
});​

To select the specific Span you need a id to be provided to that span. Else you need to loop through the list of all available span to get it.
Lets take this as Example (have added id attribute)
<p>Fruit <span class="unc_path" id="span1">\\apples\oranges\pears</span></p>
The JQuery will be like this
$('span1').text() // if you want to take the text
$('span1').html() // if you want to take the html

Related

Unable to un-highlight selection in 2 different paragraphs using Rangy library

JSfiddle for reference: https://jsfiddle.net/9jp346r4/20/
I am trying to create functionality that allows user to highlight the selected text upon pressing a button, and unhighlight the highlighted text upon right-clicking.
I've gotten it mostly working using the rangy library except there's one scenario that doesn't work and I'm not sure how to solve it.
When I highlight text that is in 2 different paragraphs, it highlights it successfully.
The issue arises when I would like to come back later and un-highlight both the paragraphs.
The expected behaviour is: I right-click any highlighted text regardless of if it is selected or not and it will un-highlight all nearby highlighted text even if it's separated by a paragraph tag or strong tag.
The current behaviour is: It only unhighlights the text in the paragraph I clicked.
To re-produce:
1) Select text that overlaps both the first and second paragraph and press the "Press" button.
2) Un-select the selected text by clicking somewhere else on the screen.
3) Right-click any of the highlighted text. Notice only one of the paragraphs gets un-highlighted.
If something is unclear, feel free to ask questions. Would appreciate the help.
Here is my HTML:
<div id="content">
<p>
Paragraph 1
</p>
<p>
Paragraph 2
</p>
</div>
<div id="divId">
<input id="myBtn" type="button" value="Press" onclick = "javascript:toggleItalicYellowBg()"/>
</div>
Here is my javascript:
function coverAll() {
var ranges = [];
for(var i=0; i<window.getSelection().rangeCount; i++) {
var range = window.getSelection().getRangeAt(i);
while(range.startContainer.nodeType == 3
|| range.startContainer.childNodes.length == 1)
range.setStartBefore(range.startContainer);
while(range.endContainer.nodeType == 3
|| range.endContainer.childNodes.length == 1)
range.setEndAfter(range.endContainer);
ranges.push(range);
}
window.getSelection().removeAllRanges();
for(var i=0; i<ranges.length; i++) {
window.getSelection().addRange(ranges[i]);
}
return true;
}
function getSelectedText() {
if (window.getSelection) {
return window.getSelection().toString();
} else if (document.selection) {
return document.selection.createRange().text;
}
return '';
}
var italicYellowBgApplier;
function toggleItalicYellowBg() {
italicYellowBgApplier.toggleSelection();
}
window.onload = function() {
$(document).on("contextmenu", ".italicYellowBg", function(e){
if(coverAll()) {
italicYellowBgApplier.undoToSelection();
return false;
}
});
rangy.init();
// Enable buttons
var classApplierModule = rangy.modules.ClassApplier;
// Next line is pure paranoia: it will only return false if the browser has no support for ranges,
// selections or TextRanges. Even IE 5 would pass this test.
if (rangy.supported && classApplierModule && classApplierModule.supported) {
italicYellowBgApplier = rangy.createClassApplier("italicYellowBg", {
tagNames: ["span", "a", "b", "img"]
});
}
};
I guess to easy solve this problem, in memory keep an array of user highlights, one item of that array is not a single highlight item, but a further "selection of selected items during the highlight", when somebody would right click on a single segment of highlight, in-memory find from array all associated highlights and unhighlight the related segments by yourself.

Javascript add class to selected text, not first occurrence of the selected text

I want to add a class (and later on to send that string to php) to a text with javascript. Whenever I try to do that, the code is adding the class to the first occurrence of my selection, not to the actual selection. Keep in mind that I want to send that EXACT selection to php (and put it in a database as well so it keep that class even after refresh).
JQ
$("#highlight").click(function(){
paraval = $('#para').html();
sel = window.getSelection();
newst = '<a class="selectedText">' + sel + '</a>';
newvalue = paraval.replace(sel, newst);
$('#para').html(newvalue);
});
HTML
<p>Will only highlight if text is selected from comment class div only</p>
<div class="comment" id="para" contenteditable="true">Here goes some text Here goes some text Here goes some text Here goes some text
Some other text</div>
<input type="button" value="Highlight" id="highlight"/>
CSS
.selectedText{
background-color:yellow;
}
.comment{
border: solid 2px;
}
.comment::selection {
background-color: yellow;
}
example here: http://jsfiddle.net/zq1dqu3o/3/
try to select the last occurrence of the word "text". the first one will get the class "selectedText"...
thanks
Call me lazy, but if you don't mind span being you selection marker tag, you can use rangy's cssApplier class.
var cssApplier;
$(document).ready(function() {
rangy.init();
cssApplier = rangy.createCssClassApplier(
"selectedText", {normalize: true,
applyToEditableOnly:true});
});
$("#highlight").click(function(){
if(cssApplier != undefined)
{
cssApplier.toggleSelection();
}
});
I use applyToEditableOnly here to make it only work in that specific div. (I'm not sure how cross-browser compatible that particular setting is. Worked in Chrome and Firefox though.) This uses position rather than selection text to decide what to mark.
JS Fiddle Here: http://jsfiddle.net/zq1dqu3o/7/
You can get the last occurence with lastIndexOf() and proceed like this:
$("#highlight").click(function(){
paraval = $('#para').text();
sel = "text";
var n = paraval.lastIndexOf(sel);
var before = paraval.substring(0,n);
newst = before + '<a class="selectedText">' + sel + '</a>';
newvalue = paraval.replace(paraval, newst);
$('#para').html(newvalue);
});
Just created a fiddle for it: Replacing last occurence
Note: This quick example is only working because the word you want to highlight is at the last position of the text, but you can check out if this solution is ok for your request. In case the last occurence of the word is elsewhere, just create a variable "after" that contains the text following the last occurence of the word to the end.
Have just provided an example for this in updated fiddle: Replacing last occurence update
with following update to previous code:
var after = paraval.substring(n + sel.length, paraval.length);
newst = before + '<a class="selectedText">' + sel + '</a>' + after;

Get all IDs of selected/highlighted DIVs

I may be going in the completely wrong direction with what I'm trying to do, so I wanted to ask for help.
Background / Overview
I need to display a paragraph of text and allow a user to select one or more words from the paragraph and save their highlighted text to a database, for just their profile. Actually, hat selection of text will eventually be (1) stored with the highlight AND (2) linked up to another set of highlighted text from another paragraph (basically, I'm tying a phrase from one source to a reference source)
What I've tried...
I have tried to put each word of the paragraph into a DIV (and a unique ID) with each DIV set to float left, so that the display looks okay.
<style>
div { float: left}
</style>
and...using an example:
<div id="GEN_1_1">
<div id="GEN_1_1_1">In</div>
<div id="GEN_1_1_2">the</div>
<div id="GEN_1_1_3">beginning</div>
<div id="GEN_1_1_4">God</div>
<div id="GEN_1_1_5">created</div>
<div id="GEN_1_1_6">the</div>
<div id="GEN_1_1_7">heaven</div>
<div id="GEN_1_1_8">and</div>
<div id="GEN_1_1_9">the</div>
<div id="GEN_1_1_10">earth</div>.
</div>
Which looks like: In the beginning God created the heaven and the earth. (minus the bold)
So far, I have used the
window.getSelection()
function to determine/grab the words that have been highlighted.
I then tried using this:
if (window.getSelection)
{
selected_len = window.getSelection().toString().length;
if (window.getSelection().toString().length>0)
{
div_id = window.getSelection().getRangeAt(0).commonAncestorContainer.parentNode.id;
}
}
to get the ID's for each DIV selected, BUT I only get a single DIV ID returned right now.
Help Request:
Is there are slick way to get the ID for each DIV selected and put it into an Array, so that I can construct a SQL query to put it into the database (the query is easy)? The selected words could total up to several hundred, if not a thousand words, so I need to make sure the solution will work with a ton of words selected.
UPDATE: JSFIDDLE DEMO
I modified the code again. See if it works for you now.
$(document).ready(function () {
$(document).on('mouseup keyup', '.checked', function () {
console.log(window.getSelection());
if (window.getSelection().toString().length>0) {
var count = window.getSelection();
var arr = [];
$('.checked span').each(function(){
var span = $(this)[0];
var isT = window.getSelection().containsNode(span, true);
if(isT){
arr.push($(this).attr('id'));
}
});
console.log(arr,count.toString());
alert(arr);
alert(count.toString());
}
});
});
I created a fiddle for solution. Check it out here: http://jsfiddle.net/lotusgodkk/GCu2D/18/
Also, I used span instead of div for the text selection. I hope that won't be an issue for you. So the code works as you want. It will return the id of the parent span in which text is selected. You can modify it to save the ID into array or as per your needs.
$(document).ready(function () {
$(document).on('mouseup', '.checked', function () {
if (window.getSelection) {
var i = getSelectionParentElement();
console.log(i);
alert('parent selected: ' + i.id);
}
});
});
function getSelectionParentElement() {
var parent = null, selection;
if (window.getSelection) {
selection = window.getSelection();
if (selection.rangeCount) {
parent = selection.getRangeAt(0).commonAncestorContainer;
if (parent.nodeType != 1) {
parent = parent.parentNode;
}
}
} else if ((selection = document.selection) && selection.type != "Control") {
parent = selection.createRange().parentElement();
}
return parent;
}

How to parse editable DIV's text with browser compatibility

I make div as editable. while i tried to parse div's text, i was needed to do the below regular expression.
innerDOM = "<div style="cursor: text;">I had downloaded all the material from the Intern,<br>You will find</div><div style="cursor: text;"> </div><div style="cursor: text;">dfvdfvdvdfvdfvdvdvdf</div><div style="cursor: text;"> </div><div style="cursor: text;">dfvdfvdvdfvdfvdfvdfvd</div>"
innerDOM.replace(/<div style="cursor: text">/g, "<br>").replace(/<\/div>/g, "");
Above regular expression works good in firefox and chrome. But not in IE. What changes should i do?
See this FIDDLE for better understanding...
DOM manipulation is one of the things jQuery was made for. Investing in learning jQuery will take you a long way towards writing DOM traversal and modification.
$("div[style='cursor: text']").unwrap().prepend("<br>");
unwrap deletes the element but keeps the children intact. The jQuery Attribute Equals Selector is used to select all divs with the cursor style attribute. You can run it live here.
You could make this more robust as currently you would not select a div with more or less whitespace or with other trivial differences. For example: <div style="cursor:text;"> is not matched by the above selector. You can work around this shortcoming by introducing a CSS class that sets the cursor. In this case <div style="cursor: text"> becomes <div class='textCursor'> and you can use the class selector: $("div.textCursor")
//FINAL ANSWER
var domString = "", temp = "";
$("#div-editable div").each(function()
{
temp = $(this).html();
domString += "<br>" + ((temp == "<br>") ? "" : temp);
});
alert(domString);
see this fiddle for answer.
i found this solution in this site:
$editables = $('[contenteditable=true'];
$editables.filter("p,span").on('keypress',function(e){
if(e.keyCode==13){ //enter && shift
e.preventDefault(); //Prevent default browser behavior
if (window.getSelection) {
var selection = window.getSelection(),
range = selection.getRangeAt(0),
br = document.createElement("br"),
textNode = document.createTextNode($("<div> </div>").text()); //Passing " " directly will not end up being shown correctly
range.deleteContents();//required or not?
range.insertNode(br);
range.collapse(false);
range.insertNode(textNode);
range.selectNodeContents(textNode);
selection.removeAllRanges();
selection.addRange(range);
return false;
}
}
});

How to change text inside span with jQuery, leaving other span contained nodes intact?

I have the following HTML snippet:
<span class="target">Change me <a class="changeme" href="#">now</a></span>
I'd like to change the text node (i.e. "Change me ") inside the span from jQuery, while leaving the nested <a> tag with all attributes etc. intact. My initial huch was to use .text(...) on the span node, but as it turns out this will replace the whole inner part with the passed textual content.
I solved this with first cloning the <a> tag, then setting the new text content of <span> (which will remove the original <a> tag), and finally appending the cloned <a> tag to my <span>. This works, but feels such an overkill for a simple task like this. Btw. I can't guarantee that there will be an initial text node inside the span - it might be empty, just like:
<span class="target"><a class="changeme" href="#">now</a></span>
I did a jsfiddle too. So, what would be the neat way to do this?
Try something like:
$('a.changeme').on('click', function() {
$(this).closest('.target').contents().not(this).eq(0).replaceWith('Do it again ');
});
demo: http://jsfiddle.net/eEMGz/
ref: http://api.jquery.com/contents/
Update:
I guess I read your question wrong, and you're trying to replace the text if it's already there and inject it otherwise. For this, try:
$('a.changeme').on('click', function() {
var
$tmp = $(this).closest('.target').contents().not(this).eq(0),
dia = document.createTextNode('Do it again ');
$tmp.length > 0 ? $tmp.replaceWith(dia) : $(dia).insertBefore(this);
});
​Demo: http://jsfiddle.net/eEMGz/3/
You can use .contents():
//set the new text to replace the old text
var newText = 'New Text';
//bind `click` event handler to the `.changeme` elements
$('.changeme').on('click', function () {
//iterate over the nodes in this `<span>` element
$.each($(this).parent().contents(), function () {
//if the type of this node is undefined then it's a text node and we want to replace it
if (typeof this.tagName == 'undefined') {
//to replace the node we can use `.replaceWith()`
$(this).replaceWith(newText);
}
});
});​
Here is a demo: http://jsfiddle.net/jasper/PURHA/1/
Some docs for ya:
.contents(): http://api.jquery.com/contents
.replaceWith(): http://api.jquery.com/replacewith
typeof: https://developer.mozilla.org/en/JavaScript/Reference/Operators/typeof
Update
var newText = 'New Text';
$('a').on('click', function () {
$.each($(this).parent().contents(), function () {
if (typeof this.tagName == 'undefined') {
//instead of replacing this node with the replacement string, just replace it with a blank string
$(this).replaceWith('');
}
});
//then add the replacement string to the `<span>` element regardless of it's initial state
$(this).parent().prepend(newText);
});​
Demo: http://jsfiddle.net/jasper/PURHA/2/
You can try this.
var $textNode, $parent;
$('.changeme').on('click', function(){
$parent = $(this).parent();
$textNode= $parent.contents().filter(function() {
return this.nodeType == 3;
});
if($textNode.length){
$textNode.replaceWith('Content changed')
}
else{
$parent.prepend('New content');
}
});
Working demo - http://jsfiddle.net/ShankarSangoli/yx5Ju/8/
You step out of jQuery because it doesn't help you to deal with text nodes. The following will remove the first child of every <span> element with class "target" if and only if it exists and is a text node.
Demo: http://jsfiddle.net/yx5Ju/11/
Code:
$('span.target').each(function() {
var firstChild = this.firstChild;
if (firstChild && firstChild.nodeType == 3) {
firstChild.data = "Do it again";
}
});
This is not a perfect example I guess, but you could use contents function.
console.log($("span.target").contents()[0].data);
You could wrap the text into a span ... but ...
try this.
http://jsfiddle.net/Y8tMk/
$(function(){
var txt = '';
$('.target').contents().each(function(){
if(this.nodeType==3){
this.textContent = 'done ';
}
});
});
You can change the native (non-jquery) data property of the object. Updated jsfiddle here: http://jsfiddle.net/elgreg/yx5Ju/2/
Something like:
$('a.changeme3').click(function(){
$('span.target3').contents().get(0).data = 'Do it again';
});
The contents() gets the innards and the get(0) gets us back to the original element and the .data is now a reference to the native js textnode. (I haven't tested this cross browser.)
This jsfiddle and answer are really just an expanded explanation of the answer to this question:
Change text-nodes text
$('a.changeme').click(function() {
var firstNode= $(this).parent().contents()[0];
if( firstNode.nodeType==3){
firstNode.nodeValue='New text';
}
})
EDIT: not sure what layout rules you need, update to test only first node, otherwise adapt as needed

Categories