I'm not sure this is possible but I wondered if there is a way of targeting the last word in a line of text within a paragraph. Note that I'm not looking for the last word in the paragraph but the last word in a line of said paragraph.
I haven't the faintest idea of how to start with this so haven't got an attempt formulated.
Thanks for any help you can offer!
Mark
I found a JS library, jsLineWrapDetector, which can retrieve the lines from the text wrapped by DOM element.
var p = $("p")[0];
var lines = lineWrapDetector.getLines(p);
lines.forEach(function(line) {
var lastword = line.split(" ").pop();
});
You can get the text of p tag, split them on occurrence of space to create the array of word. then target the last element in returned array. Like this:
var wordarray = $('p').text().split(" ");
return wordarray [wordarray.length - 1];
You can use this function :
function getLastWord(paragraph, line) {
return paragraph.split("\n")[line].split(" ").pop();
}
Working Example
The code below will find the last word of each line (of a p tag), and throw all of them in the console. words[i - 1], or breakWord is the last word of each line. Hopefully, this helps.
var breakWord
var p = $('p');
p.each(function () {
var current = $(this);
var text = current.text();
var words = text.split(' ');
current.text(words[0]);
var height = current.height();
for (var i = 1; i < words.length; i++) {
current.text(current.text() + ' ' + words[i]);
if (current.height() > height) {
height = current.height();
breakWord = words[i - 1];
console.log(breakWord);
}
}
console.log(current);
});
This code will split the whole paragraph up into single words, put them in an array (words), and then check for line breaks. When a line break is found, it returns the last word of the line.
Related
I have a long String containing a HTML document. I want to delete all the href Tags but keep the text. The following example:
Some text example 1</p> some example 2text
should become:
Some text example 1 </p> some example 2 text
The solution I found is to get all the textes, and then try to iterate again through the text and replace the tag number n with the text number n.
var a_string = 'Some text example 1</p> some example 2text',
el = document.createElement('p');
el.innerHTML = a_string;
var a = el.querySelectorAll('a');
var texts = [].slice.call(a).map(function(val){
return val.innerHTML;
});
alert(texts);
// TODO ieterate and replace occurence n with texts[n]
Is there a besser way to do this?
After first line write following code
a_string = a_string.replace(/(<a.*?>)/g,'').replace(/<\/a>/g,' ');
You can use the following Regex:
var regex = /(<\s*a([^>]+)>|<\/\s*a\s*>)/ig;
var str = 'Some text example 1</p> some example 2text';
str.replace(regex, ""); //Some text example 1</p> some example 2text
Try below regex:
var a_txt = a_string.replace(/<a[\s]+[^>]*?href[\s]?=[\s\"\']*(.*?)[\"\']*.*?>/g,"").replace(/<\/a>/g," ");
Your solution of query-selecting all a-tags isn't actually too bad. Instead of getting the text with map you could just iterate over the list and replace each element with its content. No need for regular expressions:
el.querySelectorAll('a').forEach(function( a_el ){
var text = document.createTextNode(a_el.innerText);
a_el.parentNode.replaceChild(text, a_el);
});
also, instead of "parsing" your html by putting it into a p-element, you could use a DomParser:
var parser = new DOMParser();
var doc = parser.parseFromString(a_string, "text/html");
doc.querySelectorAll('a').forEach(function( a_el ){
var text = document.createTextNode(a_el.innerText);
a_el.parentNode.replaceChild(text, a_el);
});
as stated in some answer above, your code isn't bad. I avoid the use of regular expression when not necessary.
To finish your code, you neeed to iterate through all A{ELEMENTS}. I am typing from mobile phone. Let me know if you hit an error. Thanks.
var a_string = 'Some text example 1</p> some example 2text',
el = document.createElement('p');
el.innerHTML = a_string;
var a = el.querySelectorAll('a');
for( var t = a.length - 1; t >=0 ; t-- ){
for(var c = a[t].childNodes.length - 1; c >= 0; c-- ){
if( a[t].nextSibling ){
document.insertBefore( a[t].childNodes[c], a[t].nextSibling );
} else {
a[t].parentNode.appendChild( a[t].childNodes[c]);
}
}
a[t].remove();
}
I have two textareas written in HTML like this:
<textarea id="checked-words"></textarea>
<br />
<textarea id="words" onkeyup="displayColoredText()"></textarea>
<div id="text-to-insert"></div>
(into the div element, I will insert text, using JavaScript)
My task is to write into the div section the text from the second textarea and make red the occurrences of strings from the first textarea.
Example
If the first textarea contains the following words: aaa, aab and the second contains aaab, all of the characters have to be red. If the second one contains abaa, none of the characters will be red in the div section.
Here is my JavaScript function which displays and colorize the text:
function displayColoredText() {
//Displays the colored text below the second textarea
//Find the two textareas
var firstTextArea = document.getElementById('checked-words');
var secondTextArea = document.getElementById('words');
//Split by spaces
var checkedWords = firstTextArea.value.split(" ");
var text = secondTextArea.value.split(" ");
var textToInsert = secondTextArea.value;
for(i in checkedWords) {
console.log(checkedWords.length);
textToInsert = textToInsert.replace(new RegExp(checkedWords[i], 'g'), '<span class="insertRed">' + checkedWords[i] + '</span>');
}
document.getElementById('text-to-insert').innerHTML = textToInsert;
}
My problem is, that an already replaced text won't be considered, for example, if the first textarea contains aaa and aab and if the second one contains aaab, only the first three characters will be red, instead of the whole string. How can I resolve this?
EDIT: Screenshot of the problem
Your original input from the second text area is pure text, not HTML, so this is the "state" of the data you want to do this in.
This would be my way of implementing it as mentioned in comments, recording which positions have a match first, and then simply looping over all characters in the end to wrap them in a span each:
function displayColoredText() {
//Displays the colored text below the second textarea
//Find the two textareas
var firstTextArea = document.getElementById('checked-words');
var secondTextArea = document.getElementById('words');
//Split by spaces
var checkedWords = firstTextArea.value.split(" ");
var text = secondTextArea.value;
// empty array with keys 0 to length-1 set to undefined
var markedMatches = new Array(secondTextArea.value.length);
for (var i = 0, l = checkedWords.length; i < l; ++i) {
var checkedWord = checkedWords[i],
start = 0,
matchPos;
// check for match from current starting position
while ((matchPos = text.indexOf(checkedWord, start)) !== -1) {
// mark positions from current match start to that plus length of match
for (var k = matchPos, m = matchPos + checkedWord.length; k < m; ++k) {
markedMatches[k] = true;
}
// advance starting position to continue searching
start = matchPos + 1;
}
}
var textToInsert = '';
for (var i = 0, l = text.length; i < l; ++i) {
// wrap in span if markedMatches contains true at this position
textToInsert += (markedMatches[i] ? '<span class="match">' + text[i] + '</span>' : text[i]);
}
document.getElementById('text-to-insert').innerHTML = textToInsert;
}
https://jsfiddle.net/t9xjzkaw/
As I said, you could get more sophisticated in collecting the matches as intervals, or putting multiple adjoining matching characters into a single span element, instead of wrapping each one on its own ... but this does the basic job.
Your problem is how your strings get replaced. Your first string is 'aaa aab'. After replacing for 'aaa' in 'aaab', you get '<span class="insertRed">aaa</span>b'. Trying to find 'aab' in this string will come up with no results. You have to replace from your original string and somehow combine the two. I'm not sure how to do this, but I hope this sets you on the right track.
EDIT:
I think this will work:
Instead of replacing the text in the string, place the beginning coordinate in an array and the end coordinate in a second array. Keep doing this for every word found. Then at all of the beginning coordinates, insert the string '<span class="insertRed">'. At all of the end coordinates, insert the string '<span>'. Here is the JS:
function displayColoredText() {
//Displays the colored text below the second textarea
//arrays with coordinates
var beginnings = [];
var ends = [];
//Find the two textareas
var firstTextArea = document.getElementById('checked-words');
var secondTextArea = document.getElementById('words');
//Split by spaces
var checkedWords = firstTextArea.value.split(" ");
var text = secondTextArea.value.split(" ");
var textToInsert = firstTextArea.value;
for(i in checkedWords) {
console.log(checkedWords.length);
if (firstTextArea.value.indexOf(checkedWords[i]) != -1) {
beginnings.push(firstTextArea.value.indexOf(checkedWords[i]));
ends.push(firstTextArea.value.indexOf(checkedWords[i]) + checkedWords[i].length);
}
}
beginnings.sort(function(a, b){return b-a});
ends.sort(function(a, b){return b-a});
for (b in beginnings) {
textToInsert = textToInsert.slice(0, ends[b]) + "</span>" + textToInsert.slice(ends[b]);
textToInsert = textToInsert.slice(0, beginnings[b]) + '<span class="insertRed">' + textToInsert.slice(beginnings[b]);
}
document.getElementById('text-to-insert').innerHTML = textToInsert;
}
This code is untested, so tell me if something doesn't work and I will change it. Basically, what I am doing is instead of replacing occurrences, I find them first, place them in arrays, and insert the correct text at those coordinates. I hope this helps!
Hi I'm still a newbie at javascript so I want to create a script that inserts a line break after every 3 lines. So here's my code I got so far
var num = `http://url.com
http://url2test.com
http://url3nag.com
http://url4lalala.com
http://url5papapapapa.com
http://url6ddadadadad.com
http://url7etet.com
http://url8testtest.com`;
var newNum = num.toString().match(/.{3}/g).join('</br>');
console.log(newNum);
It is doing it wrong. It seems to be inserting every 3characters instead of lines. Can anyone help me fix the code?
You can use the replace function. Try the below code.
var num = `http://url.com
http://url2test.com
http://url3nag.com
http://url4lalala.com
http://url5papapapapa.com
http://url6ddadadadad.com
http://url7etet.com
http://url8testtest.com`;
var newNum = num.replace(/(.*\n.*\n.*\n)/g, '$1<br>');
console.log(newNum);
EDIT
I have made a few changes to the RegEx in the code below. This will allow you to specify the number of lines between which <br> need to be added.
var num = `http://url.com
http://url2test.com
http://url3nag.com
http://url4lalala.com
http://url5papapapapa.com
http://url6ddadadadad.com
http://url7etet.com
http://url8testtest.com`;
var newNum = num.replace(/((.*\n){3})/g, '$1<br>');
console.log(newNum);
In the above RegEx, the .* will match all characters till the end of line and the \n will match the new line character.
(.*\n){3}
I have enclosed this in parenthesis to mark it as a group and used {3} to indicate that the preceding group repeats 3 times.
((.*\n){3})
Then the whole RegEx is enclosed in a parenthesis to use it as the first matched group that can be referenced in the replace section using $1.
You can replace the {3} with any number.
You should avoid using string manipulation when using HTML string. Also using BR to break line is not a good idea as well. You should use a block element instead.
var num = `http://url.com
http://url2test.com
http://url3nag.com
http://url4lalala.com
http://url5papapapapa.com
http://url6ddadadadad.com
http://url7etet.com
http://url8testtest.com`;
var content = document.querySelector('.content');
var urls = num.split('\n');
var temp;
for(var i = 0; i< urls.length; i++) {
if(!temp || (i+1) % 3 === 0) {
if (temp) content.appendChild(temp);
temp = document.createElement('div');
}
var span = document.createElement('span');
span.classList.add('link')
span.innerHTML = urls[i];
temp.appendChild(span);
}
content.appendChild(temp);
.link {
margin: 5px;
}
<div class='content'>
Reference:
Is it sometimes bad to use <BR />?
Given this HTML as a string "html", how can I split it into an array where each header <h marks the start of an element?
Begin with this:
<h1>A</h1>
<h2>B</h2>
<p>Foobar</p>
<h3>C</h3>
Result:
["<h1>A</h1>", "<h2>B</h2><p>Foobar</p>", "<h3>C</h3>"]
What I've tried:
I wanted to use Array.split() with a regex, but the result splits each <h into its own element. I need to figure out how to capture from the start of one <h until the next <h. Then include the first one but exclude the second one.
var html = '<h1>A</h1><h2>B</h2><p>Foobar</p><h3>C</h3>';
var foo = html.split(/(<h)/);
Edit: Regex is not a requirement in anyway, it's just the only solution that I thought would work for generally splitting HTML strings in this way.
In your example you can use:
/
<h // Match literal <h
(.) // Match any character and save in a group
> // Match literal <
.*? // Match any character zero or more times, non greedy
<\/h // Match literal </h
\1 // Match what previous grouped in (.)
> // Match literal >
/g
var str = '<h1>A</h1><h2>B</h2><p>Foobar</p><h3>C</h3>'
str.match(/<h(.)>.*?<\/h\1>/g); // ["<h1>A</h1>", "<h2>B</h2>", "<h3>C</h3>"]
But please don't parse HTML with regexp, read RegEx match open tags except XHTML self-contained tags
From the comments to the question, this seems to be the task:
I'm taking dynamic markdown that I'm scraping from GitHub. Then I want to render it to HTML, but wrap every title element in a ReactJS <WayPoint> component.
The following is a completely library-agnostic, DOM-API based solution.
function waypointify(html) {
var div = document.createElement("div"), nodes;
// parse HTML and convert into an array (instead of NodeList)
div.innerHTML = html;
nodes = [].slice.call(div.childNodes);
// add <waypoint> elements and distribute nodes by headings
div.innerHTML = "";
nodes.forEach(function (node) {
if (!div.lastChild || /^h[1-6]$/i.test(node.nodeName)) {
div.appendChild( document.createElement("waypoint") );
}
div.lastChild.appendChild(node);
});
return div.innerHTML;
}
Doing the same in a modern library with less lines of code is absolutely possible, see it as a challenge.
This is what it produces with your sample input:
<waypoint><h1>A</h1></waypoint>
<waypoint><h2>B</h2><p>Foobar</p></waypoint>
<waypoint><h3>C</h3></waypoint>
I'm sure someone could reduce the for loop to put the angle brackets back in but this is how I'd do it.
var html = '<h1>A</h1><h2>B</h2><p>Foobar</p><h3>C</h3>';
//split on ><
var arr = html.split(/></g);
//split removes the >< so we need to determine where to put them back in.
for(var i = 0; i < arr.length; i++){
if(arr[i].substring(0, 1) != '<'){
arr[i] = '<' + arr[i];
}
if(arr[i].slice(-1) != '>'){
arr[i] = arr[i] + '>';
}
}
Additionally, we could actually remove the first and last bracket, do the split and then replace the angle brackets to the whole thing.
var html = '<h1>A</h1><h2>B</h2><p>Foobar</p><h3>C</h3>';
//remove first and last characters
html = html.substring(1, html.length-1);
//do the split on ><
var arr = html.split(/></g);
//add the brackets back in
for(var i = 0; i < arr.length; i++){
arr[i] = '<' + arr[i] + '>';
}
Oh, of course this will fail with elements that have no content.
Hi I used this function to convert html String Dom in array
static getArrayTagsHtmlString(str){
let htmlSplit = str.split(">")
let arrayElements = []
let nodeElement =""
htmlSplit.forEach((element)=>{
if (element.includes("<")) {
nodeElement = element+">"
}else{
nodeElement = element
}
arrayElements.push(nodeElement)
})
return arrayElements
}
Happy code
I am trying to create a regular expression in Javascript to separate any occurrence of :) from the surrounding text.
Given the following:
:)This is a line of text :) with several smileys in it :). So there.,:):)
I would like to get the resulting 8 groups:
:)
This is a line of text
:)
with several smileys in it
:)
. So there.,
:)
:)
At the moment I use ([^:)]+) which only groups the surrounding text without the smileys. What adjustment could I make so that the smileys are also grouped?
I'd suggest:
var str = ":)This is a line of text :) with several smileys in it :). So there.,:):)",
matches = str.split(/(\:\))/);
console.log(matches);
JS Fiddle demo.
Added filtering to remove the empty matches from the above:
var str = ":)This is a line of text :) with several smileys in it :). So there.,:):)",
matches = str.split(/(\:\))/),
matched = [];
for (var i = 0, len = matches.length; i < len; i++) {
if (matches[i].length) {
matched.push(matches[i]);
}
}
console.log(matched);
JS Fiddle demo.
A further version, with actual on-screen output:
var str = ":)This is a line of text :) with several smileys in it :). So there.,:):)",
matches = str.split(/(\:\))/),
matched = [], li,
list = document.createElement('ol');
document.body.appendChild(list);
for (var i = 0, len = matches.length; i < len; i++) {
if (matches[i].length) {
matched.push(matches[i]);
li = document.createElement('li');
txt = document.createTextNode(matches[i]);
li.appendChild(txt);
list.appendChild(li);
}
}
console.log(matched);
JS Fiddle demo.
split:
var string = "Given the following: :)This is a line of text :) with several smileys in it :). So there.,:):)";
var groups = string.split(/:\)/);
This will return an array in which each element is a part of text without :).
Since the text has been split on each :), we can construct the result you want by adding a :) between each parts of text:
var parts = [groups[0]];
for (var i = 1; i < groups.length; ++i) {
parts.push(':)');
parts.push(groups[i]);
}
The result is this:
:)
This is a line of text
:)
with several smileys in it
:)
. So there.,
:)
:)
Try this here: http://jsfiddle.net/Gxr6U/3/
If you want to replace smilies by images, you could do this:
var frags = document.createDocumentFragment();
frags.appendChild(document.createTextNode(groups[0]));
for (var i = 1; i < groups.length; ++i) {
var img = document.createElement('img');
img.src='http://../smiley.png';
frags.push(img);
frags.appendChild(document.createTextNode(groups[i]));
}
If you just want to remove smilies:
var text = groups.join('');
The following will successfully group the string you mentioned as you want:
your_match = your_string.match(/(:\)|[^:)]*)/g)
However, like your original RegExp, you will encounter problems if a : or ) appears without being in a smiley. These characters will disappear from the match groups if not within a smiley.
You can use this pattern:
/:\)|(?:[^:]+|:(?!\)))+/g
This matches either :) or any character except : or a : that is not followed a ).