javascript to get paragraph of selected text in web page - javascript

After highlighting text, I would like to obtain the paragraph in which the selected text resides.
var select = window._content.document.getSelection();
Any pointers please?

This is actually rather hard to do because you have to consider six cases:
The selection is not within a paragraph (easy);
The entire selection is within one paragraph (easy);
The entire selection crosses one or more sibling paragraphs (harder);
The selection starts or ends in an element not within a paragraph (harder);
The paragraphs spanned are at different levels eg one is within a list item while two others are siblings of the list (even harder); and
Some combination of the above.
So firstly you have to decide how complete you want the solution to be. I'll only cover the simplest cases of (1) and (2).
function getSelectedParagraphText() {
if (window.getSelection) {
selection = window.getSelection();
} else if (document.selection) {
selection = document.selection.createRange();
}
var parent = selection.anchorNode;
while (parent != null && parent.localName != "P") {
parent = parent.parentNode;
}
if (parent == null) {
return "";
} else {
return parent.innerText || parent.textContent;
}
}
Note: If you're after tags too replace textContent with innerHTML.
Edit: Better version put in, including better browser compatibility.

I found this useful example.
It seems that some browsers support window.getSelection() while others support document.getSelection(). The example handle all these cases.

select.anchorNode.parentNode will return the parent node, in your case the tag and you can then get the text of that node.
var x = window.getSelection()
var z = x.anchorNode.parentNode
alert(z.innerHTML)
Make sure you look at window.getSelection() as well since document.getSelection is depreciated in firefox.

$.event.props.push('onTextSelect');
$(document).click(function(){
var str=window.getSelection().anchorNode.data;
var str=str.substring(window.getSelection().anchorOffset,window.getSelection().focusOffset);
if(str)
$(window.getSelection().focusNode.parentNode).trigger({type:'onTextSelect',text:str});
});
$('p').on('onTextSelect',function(e){
console.log($(this).attr('class'))
$('p:last').text(e.text);
});
html
<div><p class="p">some text</p></div>
<p></p>
You can find the fiddle here https://jsfiddle.net/q9nkc0fd/6/

A new project is born from jsmatita:
http://takenotes.sourceforge.net/
(it's in italian language)

I reproduced code from answer above from #Thiago Souza and created a snippet for that purpose for those who can provide a correct answer.
function getSelectedParagraph(){
const selection = window.getSelection();
let parent = selection.anchorNode;
while (parent != null && parent.nodeName != "P") {
parent = parent.parentNode;
};
console.log(parent);
return parent;
};
window.onload = getSelectedParagraph();
<div class='paragraph-container'>
<p id='paragraph-01'>
Paragraph 1: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
<p id='paragraph-02'>
Paragraph 2: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
<p id='paragraph-03'>
Paragraph 3: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
</div>

function getSelectedParagraph(){
const selection = window.getSelection();
let parent = selection.anchorNode;
while (parent != null && parent.nodeName != "P") {
parent = parent.parentNode;
}
return parent;
}

Related

How to get textContent including childNodes?

I have some plain text content in paragraphs inside a <main> HTML element.
the paragraphs are separated by new lines (\n), not in <p> tags, and I would like to automatically wrap them in <p> tags using JavaScript.
Example content:
<main>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore
eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
<img src="img/testimg.jpg"> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</main>
Inside of <main> there may be <img> elements.
I want the script to watch for these and leave them untouched. It breaks the HTML (no image is rendered, and dev tools show overflow) if the script tries to wrap them like: <p><img src="img/testimg.jpg"></p> (assuming that is what it is doing).
My script so far:
var maintext = document.getElementsByTagName('main')[0]; // get the first (0th) <main> element
var arr = maintext.textContent.split(/[\r?\n]+/); // split the text inside into an array by newline
// regex matches one OR MORE consecutive newlines, which prevents empty <p></p> being captured
arr.forEach(function(part, index) {
if (!this[index].includes("<img ")) {
this[index] = "<p>" + this[index] + "</p>"; // wrap each paragraph with <p> tags
}
}, arr);
var rejoined = arr.join("\r\n"); // join the array to remove commas, with newlines as separators
maintext.innerHTML = rejoined; // replace contents of our <main> element with the new text
I believe the problem may be that <img> is not captured as text along with the textContent of <main>, but instead remains recognized as a child node and it's messing up the array.
You can see my attempt to only wrap in <p> if that array element does not (!) contain "<img " ...however, this is not working. It seems the enclosed HTML elements do not get matched as string data by includes.
What's the best way to go about this?
To retain non-text content like images, you'll need to process the text nodes of the main element rather than using textContent, since that's just the text content of the element.
Assuming you only want to do this with the text nodes, you can loop through the element's nodes, split text nodes on line breaks, and if you get more than one segment, insert paragraphs for them. Something like this (see inline comments):
function convertLineBreaksToParagraphs(element) {
// Get a snapshot of the child nodes of the element; we want
// a snapshot because we may change the element's contents
const nodes = [...element.childNodes];
// Loop through the snapshot
for (const node of nodes) {
// Is this a text node?
if (node.nodeType === Node.TEXT_NODE) {
// Yes, split it on line breaks
const parts = node.nodeValue.split(/\r\n|\r|\n/);
// Did we find any?
if (parts.length > 1) {
// Yes, loop through the "paragraphs"
for (const part of parts) {
// Create an actual paragraph for it
const p = document.createElement("p");
p.textContent = part;
// Insert in in front of the text node it came from
element.insertBefore(p, node)
}
// Remove the text node we've replaced with paragraphs
element.removeChild(node);
}
}
}
}
convertLineBreaksToParagraphs(document.querySelector("main"));
<main>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore
eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
<img src="img/testimg.jpg"> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</main>
You may need to tweak that a bit, depending on how you want to handle that image just before the fifth paragraph. The above leaves the image outside the paragraph. But if you wanted it to be inside the paragraph, you could add some logic to do that.
function convertLineBreaksToParagraphs(element) {
let img = null;
// Get a snapshot of the child nodes of the element; we want
// a snapshot because we may change the element's contents
const nodes = [...element.childNodes];
// Loop through the snapshot
for (const node of nodes) {
// Is this a text node?
if (node.nodeType === Node.TEXT_NODE) {
// Yes, split it on line breaks
const parts = node.nodeValue.split(/\r\n|\r|\n/);
// Did we find any?
if (parts.length > 1) {
// Yes, loop through the "paragraphs"
for (const part of parts) {
// Create an actual paragraph for it
const p = document.createElement("p");
p.textContent = part;
// If we *just* saw an image before this text node,
// move it into the paragraph
if (img) {
p.insertBefore(img, element.firstChild);
img = null;
}
// Insert in in front of the text node it came from
element.insertBefore(p, node)
}
// Remove the text node we've replaced with paragraphs
element.removeChild(node);
}
} else if (node.nodeName === "IMG") {
img = node;
} else {
img = null;
}
}
}
convertLineBreaksToParagraphs(document.querySelector("main"));
<main>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore
eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
<img src="img/testimg.jpg"> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</main>
You may have people telling you to do this by manipulating the HTML from innerHTML, but the problem with doing that is you run the risk of introducing tags in the middle of a tag (and you will remove any event handlers when you set innerHTML on main). For instance, if you have:
<img
src="/path/to/something">
you'd end up with
<img
<p> src="/path/to/something"></p>
...which is obviously not good.

Highlighting text based on its match with another string

I'm trying to highlight text based on whether it's matched with a string recieved from a database or not. To show the problem, here's an example.
Given this text:
<div class='text'>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</div>
And this text:
<div id='matchingHighlight'>
Lorem ipsum <!-- Highlight this text within <div id='text'></div> !-->
</div>
How would I highlight the text within .text that matches with the text from #matchingHighlight (Lorem ipsum)?
To 'highlight' the keyword / phrases, you will need to wrap it around any inline element. I'm suggesting you use <span> tag.
The following logic will replace the text with the same text wrapped inside a span tag with a class of .highlight
$(document).ready(function() {
var $text = $('.text');
var textToHighlight = $('#matchingHighlight').text().trim();
var textCurrent = $text.html().trim();
var isTextExists = textCurrent.indexOf(textToHighlight) > -1;
if(isTextExists) {
textCurrent = textCurrent.replace(textToHighlight, "<span class='highlight'>" + textToHighlight + "</span>");
$text.html(textCurrent);
}
});
You can then style it using css, for example
.highlight {
background: yellow;
}
https://pastebin.com/vaB3wa96

Retrieving content within first paragraph tag with JavaScript RegEx

I am trying to return only the text within the first tags, stripping out the tags themselves...
The HTML is all within a string from an API, so I am escaping it in a React app, but I want to strip out the HTML to teaser a paragraph. Will update post.
"content": {
"rendered": "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>\n",
"protected": false
},
looking at other threads, the below should work, but I don't know how to populate the 2nd arg to replace with the content of the p tags:
string.replace(/<p>(.*?)<\/p>/);
Also, is there a way to limit it to the first paragraph if there is?
Thank you
You can use this to replace any html tag with it's innerHTML
or this
const getInnerHTML = (data) => {
const match = data.match(/<(\w+)>(.*?)<\/\1>/)
if (match){
return match[2]
} else throw new Error("not valid data")
}
console.log(getInnerHTML(`<div>this is innerHTML of div</div>`))
console.log(getInnerHTML(`<p>this is innerHTML of p</p>`))
console.log(getInnerHTML(`<p>this is innerHTML of p`))
Then don't replace, instead get it :
var str = '<p>Test Text</p>';
var m = str.match(/<p>([\s\S]*)?<\/p>/i)||[];
str = m[1] || '';
console.log(str);

AngularJS ng-show is not working

I'm trying to make the button "Detalhes" to toggle a div to show a message.
Apparently there's nothing wrong.
First... my HTML
<tr ng-repeat="chamado in cabertos">
<td>{{chamado.numero}}</td>
<td>{{chamado.user}}</td>
<td>{{chamado.assunto}}</td>
<td>{{chamado.status_chamado}}</td>
<td><button ng-click="mostra()">Detalhes</button></td>
</tr>
</tbody>
<div ng-show="{{visivel}}">
<h3>Mensagem enviada:</h3>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</div>
</table>
</div>
My Script:
app.controller('mostra',function($scope){
$scope.visivel = false;
$scope.mostra = function() {
if($scope.visivel==false) $scope.visivel=true;
else if($scope.visivel == true) $scope.visivel=false;
};
});
And the when I press F12 in my page, for an unknown reason there is a ng-hide not allowing me to toggle my div:
<div ng-show="false" class="ng-hide">
<h3>Mensagem enviada:</h3>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</div>
Change
<div ng-show="{{visivel}}">
To
<div ng-show="visivel">
Edit - adding explanation why this is the case. I am quoting Evan, as I could not explain this any better than he.
Why is this?
The $scope.visive1 variable does not need to be interpolated through
the use of double-brackets in the ng-show directive. In short
Directives do not need braces, while expressions DO need them
- #Evan Bechtol
You should do this
ng-show="visivel"
and not
ng-show="{{visivel}}"
Reason why you see class="ng-hide"
since your ng-show is false, angular applies class ng-hide which hides the element as it is opposite of show, if ng-show was true it would have removed the class.
Also you do not need to use the curly braces (interpolation) along with pre defined angular JS directives like ng-show, ng-hide, ng-if, ng-repeat. Angular knows by itself what you passing to these directives
When you refresh the page you reset the App and therefore you do this:
$scope.visivel = false;
making the div invisible..
Try initializing $scope.visivel = {} in the controller.
app.controller('mostra',function($scope){
$scope.visivel = {};
$scope.visivel = false;
$scope.mostra = function() {
if($scope.visivel==false) $scope.visivel=true;
else if($scope.visivel == true) $scope.visivel=false;
};
});
Also in the HTML use
ng-show="visivel" instead of ng-show="{{visivel}}"

Javascript word highlighting in a text

I have a variable called output in Javascript, which has the following content.
var output = "something."
I want to search and highlight the words "word1" and "word2" in them when I display the content of output. The above content is dynamic. Assuming I have a variable output which has the text and an array called arr1 which has the elements to be searched and highlighted, how can I display the entire content with words highlighted in javascript?Please let me know. Thanks in advance.
Assuming you want literal phrases that are generally just words (no special characters or punctuation), you could use a regular expression similar to this (I've also added case insensitivity):
var output = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
var arr1 = ['eiusmod tempor', 'consectetur'];
var regex = new RegExp('('+arr1.join('|')+')', 'gi');
output = output.replace(regex, "<b>$1</b>");
// the following line is for debug purposes only. I've added it
// to better display what's happening just for the Stackoverflow
// code snippet editor.
document.body.innerHTML = output;
b {
background: yellow;
font-weight: normal;
}
Edit:
Quick edit to include logic that ensures "consectrtur" is matched, but "consecteturs" is not. It just needs a simple look ahead of (?!\\w) and a "look behind" (not a real look behind because javascript doesn't support it) of (\\W+|^) ensuring the matched term is not surrounded by word characters, and thus not part of a different word.
var output = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse consecteturs cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum";
var arr1 = ['eiusmod tempor', 'consectetur', 'orem', 'laborum'];
var regex = new RegExp('(\\W+|^)('+arr1.join('|')+')(?!\\w)', 'gi');
output = output.replace(regex, "$1<b>$2</b>");
// the following line is for debug purposes only. I've added it
// to better display what's happening just for the Stackoverflow
// code snippet editor.
document.body.innerHTML = output;
b {
background: yellow;
font-weight: normal;
}
You can wrap the words you want highlighted with a <span> tag then add a class or inline styling to the tag for your highlight color. This would require that you identify the words you want highlighted and add the opening tag before and closing tag after with a String.replace(word, tag + word + closing_tag) or something similar.
To wrap something in HTML using Javascript, you should be looking at Regex. For example:
str = str.replace(/(neoplasm)/ig, "<b>$1</b>");
To replace from an array, you could do something like connecting your strings with a join using the regex OR: | to generate a custom regex string, matching those words.
EDIT: Like Joseph just suggested, just one minut before me.

Categories

Resources