Manipulate html-string variable with javascript - javascript

I need to manipulate a string-variable with JavaScript, which has some html-content. I want to search for some elements, change them and wrap these elements with another div-container.
How can I get this:
var myArr = ['Foo', 'Bar'];
var contenthtml = "<p>Foo</p>
<p>Lorem ipsum dolor sit amet</p>
<p>Lorem <b>ipsum</b> dolor sit amet</p>
<p>Lorem ipsum dolor sit amet</p>
<p>Bar</p>
<p>Lorem ipsum dolor sit amet</p>";
to this:
contenthtml = "<div class='foo'><h1>Foo</h1>
<p>Lorem ipsum dolor sit amet</p>
<p>Lorem <b>ipsum</b> dolor sit amet</p>
<p>Lorem ipsum dolor sit amet</p></div>
<div class='bar'><h1>Bar</h1>
<p>Lorem ipsum dolor sit amet</p></div>";

You can use a regular expression (similar to my other answer at https://stackoverflow.com/a/21803683/3210837):
var keywordsRegEx = keywords.map(function(x){return x.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');}).join('|');
var result = str.replace(new RegExp('<p>(' + keywordsRegEx + ')</p>\r\n((?:[ \t]*<p>(?:(?!' + keywordsRegEx + ').)*</p>(?:\r\n)?)*)', 'mgi'), '<div><h1 class="$1">$1</h1>\r\n$2</div>\r\n');
See http://jsfiddle.net/ncu43/1/ for a full example.
What the regular expression does is it matches <p>, one of the keywords, </p>, and then a paragraph (not containing one of the keywords) zero or more times.

I used some DOM to solve this problem. For those who prefer a DOM solution, rather than RegExp:
Append elements in a variable instead of a temporary DOM

This would be a little bit easier with straight-up DOM replacement/wrapping (in fact, I wrote such a solution but re-wrote it when I saw your comment saying you needed string input), but here's a solution using just a string as input:
var myArr = ['Foo', 'Bar'];
var contenthtml = '<p>Foo</p>\n'
+ '<p>Lorem ipsum dolor sit amet</p>\n'
+ '<p>Lorem <b>ipsum</b> dolor sit amet</p>\n'
+ '<p>Lorem ipsum dolor sit amet</p>\n'
+ '<p>Bar</p>\n'
+ '<p>Lorem ipsum dolor sit amet</p>';
var elements = $.parseHTML(contenthtml);
var tmp = '';
$.each(elements, function(index, element) {
$.each(myArr, function(i, e) {
if (element.innerHTML == e) {
elements[index] = $('<h1>' + e + '</h1>').get(0);
return;
}
});
if (elements[index].outerHTML) {
tmp += elements[index].outerHTML + '\n';
}
});
contenthtml = '<div class="foo">' + tmp + '</div>';
console.log(contenthtml);
jsfiddle

Related

Using .split to split up content in a string

Hi I've got a string where I want to spilt up the content "ipsum dolar" and wrap it into a span tag and have the background change to red. My code does this but it wraps the two words into separate span tags. How would i amend my code to wrap them into one span tag together? Any help on this would be appreciated.
var findWords = 'ipsum dolor';
var elem = document.querySelectorAll('p.content');
elem.forEach(function(el) {
el.innerHTML = el.textContent.split(' ').map(function(i) {
return findWords.indexOf(i) > -1 ? '<span class="matched">' + i + '</span>' : i;
}).join(' ');
});
.matched {background: red;}
<p class="content"> Lorem ipsum dolor sit amet</p>
<p class="content"> Lorem ipsum dolor sit amet</p>
Using .replace() will be better in this case:
var findWords = 'ipsum dolor';
var elem = document.querySelectorAll('p.content');
elem.forEach(function(el) {
el.innerHTML = el.textContent.replace(new RegExp(findWords, 'g'), '<span class="matched">' + findWords + '</span>');
});
.matched {background: red;}
<p class="content"> Lorem ipsum dolor sit amet</p>
<p class="content"> Lorem ipsum dolor sit amet ipsum dolor</p>

JavaScript: How to generate nested ordered list

How to generate nested ordered lists from the following content? I have searched the forum and worked for a few hours now to generate ordered lists based on the different classes from the source content. The content may have up to 6 nesting
level.
What I need is to generate ordered lists based on the different classes. As shown in the sample content to get something like below outlined example content.
.firstclass => 1.
.secondclass => 1.
.thirdclass => 1.
.fourthclass => 1.
The code:
var cheerio = require('cheerio');
var $ = cheerio.load('<h1 class="header">First Header</h1><p class="firstclass">First Lorem ipsum dolor sit.</p><p class="firstclass">First Qui consequatur labore at.</p><p class="secondclass">Second Lorem ipsum dolor sit amet.</p> <p class="thirdclass">Third Lorem ipsum dolor sit amet.</p><p class="thirdclass">Third Molestias optio quasi ipsam unde!</p><p class="secondclass">Second Lorem ipsum dolor sit amet, consectetur.</p><p class="fourthclass">Fourth Lorem ipsum dolor sit.</p><p class="firstclass">First Lorem ipsum dolor sit amet.</p>', {
normalizeWhitespace: true,
xmlMode: true,
decodeEntities: false,
});
var myContent = $('p').each(function() {
var para = $(this).text();
return para;
});
var olClass = ['.firstclass', '.secondclass', '.thirdclass', '.fourthclass'];
function arrToOl(arr) {
var ol = $('<ol />'),
li = $('<li />');
for (var i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
li.append(arrToOl(arr[i]));
} else {
li = $('<li />');
li.append($(arr[i]));
ol.append(li);
}
}
return $.html(ol);
}
console.dir(arrToOl(olClass));
The above code produces the following:
'<ol><li><p class="firstclass">First Lorem ipsum dolor sit.</p><p class="firstclass">First Qui consequatur labore at.</p><p class="firstclass">First Lorem ipsum dolor sit amet.</p></li><li><p class="secondclass">Second Lorem ipsum dolor sit amet.</p><p class="secondclass">Second Lorem ipsum dolor sit amet, consectetur.</p></li><li><p class="thirdclass">Third Lorem ipsum dolor sit amet.</p><p class="thirdclass">Third Molestias optio quasi ipsam unde!</p></li><li><p class="fourthclass">Fourth Lorem ipsum dolor sit.</p></li></ol>'
The desired result should be:
<ol>
<li>
<p class="firstclass">First Lorem ipsum dolor sit.</p>
</li>
<li>
<p class="firstclass">First Qui consequatur labore at.</p>
</li>
<li>
<p class="firstclass">First Lorem ipsum dolor sit amet.</p>
<ol>
<li>
<p class="secondclass">Second Lorem ipsum dolor sit amet.</p>
</li>
<li>
<p class="secondclass">Second Lorem ipsum dolor sit amet, consectetur.</p>
<ol>
<li>
<p class="thirdclass">Third Lorem ipsum dolor sit amet.</p>
</li>
<li>
<p class="thirdclass">Third Molestias optio quasi ipsam unde!</p>
<ol>
<li>
<p class="fourthclass">Fourth Lorem ipsum dolor sit.</p>
</li>
</ol>
</li>
</ol>
</li>
</ol>
</li>
</ol>
Your help is really appreciated.
Here's what I got.
let array = ["a", "b", "c", "d"];
var nested;
function create_nested()
{
var old_ol;
for (let i = 0; i < array.length; i++)
{
let new_ol = document.createElement("ol");
let new_li = document.createElement("li");
new_li.innerHTML = array[i];
new_ol.appendChild(new_li);
if (i !== 0)
{
let nest_li = document.createElement("li");
let new_p = document.createElement("p");
new_p.innerHTML = "new stuff";
nest_li.appendChild(new_p);
nest_li.appendChild(old_ol);
new_ol.appendChild(nest_li);
}
old_ol = new_ol;
nested = new_ol;
}
}
create_nested();
document.getElementById('main').appendChild( nested);
<div id='main'>
</div>
This is just an example and not exactly the data that you have (you can figure that out).
What's happening is that I'm creating new elements using document.createElement, after which I am inserting them into their corresponding ol/li using appendChild.
The most important part is the if (i !== 0) (Change this to suit whether you want to start from the beginning or end of your array). This is the part where I am creating the nests.
I am creating a new li, which has the <p> and the old_ol which is the nesting li. So what this function is doing, is creating the innermost ol, and expanding it upward.
There might be a clear/better way of doing this, but this is as far as I know in vanilla JS. I hope everything is clear enough.

Javascript reg exp between closing tag to opening tag

How do I select with Regular Expression the text after the </h2> closing tag until the next <h2> opening tag
<h2>my title here</h2>
Lorem ipsum dolor sit amet <b>with more tags</b>
<h2>my title here</h2>
consectetur adipisicing elit quod tempora
In this case I want to select this text: Lorem ipsum dolor sit amet <b>with more tags</b>
Try this: /<\/h2>(.*?)</g
This finds a closing tag, then captures anything before a new opening tag.
in JS, you'd do this to get just the text:
substr = str.match(/<\/h2>(.*?)<h2/)[1];
Regex101
var str = '<h2>my title here</h2>Lorem ipsum <b>dolor</b> sit amet<h2>my title here</h2>consectetur adipisicing elit quod tempora';
var substr = str.match(/<\/h2>(.*?)<h2/)[1].replace(/<.*?>/g, '');
console.log(substr);
//returns: Lorem ipsum dolor sit amet
Try
/<\/h2>((?:\s|.)*)<h2/
And you can see it in action on this regex tester.
You can see it in this example below too.
(function() {
"use strict";
var inString, regEx, res, outEl;
outEl = document.getElementById("output");
inString = "<h2>my title here</h2>\n" +
"Lorem ipsum dolor sit amet <b>with more tags</b>\n" +
"<h2> my title here </h2>\n" +
"consectetur adipisicing elit quod tempora"
regEx = /<\/h2>((?:\s|.)*)<h2/
res = regEx.exec(inString);
console.log(res);
res.slice(1).forEach(function(match) {
var newEl = document.createElement("pre");
newEl.innerHTML = match.replace(/</g, "<").replace(/>/g, ">");
outEl.appendChild(newEl);
});
}());
<main>
<div id="output"></div>
</main>
I added \n to your example to simulate new lines. No idea why you aren't just selecting the <h2> with a querySelector() and getting the text that way.
Match the tags and remove them, by using string replace() function. Also this proposed solution removes any single closure tags like <br/>,<hr/> etc
var htmlToParse = document.getElementsByClassName('input')[0].innerHTML;
var htmlToParse = htmlToParse.replace(/[\r\n]+/g,""); // clean up the multiLine HTML string into singleline
var selectedRangeString = htmlToParse.match(/(<h2>.+<h2>)/g); //match the string between the h2 tags
var parsedString = selectedRangeString[0].replace(/((<\w+>(.*?)<\/\w+>)|<.*?>)/g, ""); //removes all the tags and string within it, Also single tags like <br/> <hr/> are also removed
document.getElementsByClassName('output')[0].innerHTML += parsedString;
<div class='input'>
<i>Input</i>
<h2>my title here</h2>
Lorem ipsum dolor sit amet <br/> <b>with more tags</b>
<hr/>
<h2>my title here</h2>
consectetur adipisicing elit quod tempora
</div>
<hr/>
<div class='output'>
<i>Output</i>
<br/>
</div>
Couple of things to remember in the code.
htmlToParse.match(/(<h2>.+<h2>)/g); returns an array of string, ie all the strings that was matched from this regex.
selectedRangeString[0] I am just using the first match for demo purspose. If you want to play with all the strings then you can just for loop it with the same logic.

How can one eliminate redundant, repeated HTML tags via Javascript?

I have some HTML that comes back from another process looking like this:
Lorem <i style="color:blue;">
<strong>ipsum</strong>
</i>
<i style="color:blue;">
<strong> </strong></i>
<i style="color:blue;">
<strong>test</strong>
</i> dolor sit amet
Note that basically every element (where a word, group of punctuation, or group of whitespace constitutes an "element") has its own set of identical tags wrapped around it. I am trying to find a way in Javascript to simplify it back to this:
Lorem <i style='color:blue;'>
<strong>ipsum test</strong></i>
dolor sit amet
It seems at once both simple and complex. My brain is fried from a full day of power-coding, so I'm not coming up with any creative solutions.
Superthanks!
How about
DEMO
var str = 'Lorem <i style="color:blue;"><strong>ipsum</strong></i><i style="color:blue;"><strong> </strong></i><i style="color:blue;"><strong>test</strong></i> dolor sit amet'
var d = document.createElement('div');
d.innerHTML= str;
var italics = d.getElementsByTagName('i');
var text = str.substring(0,str.indexOf('<i'))
text += '<i style="color:blue;"><strong>';
for (var i=0;i<italics.length;i++) {
text += italics[i].textContent;
}
text += '</strong></i>';
text += str.substring(str.lastIndexOf('>')+1);
console.log(text)
document.getElementById('content').innerHTML=text;
// You have to be really sure of the source:
// (the lines in the string are broken for posting)
var s= 'Lorem <i style="color:blue;"><strong>ipsum</strong></i>'+
'<i style="color:blue;"><strong> </strong></i><i style="color:blue;">'+
'<strong>test</strong></i> dolor sit amet';
s= s.replace(/<\/strong>\s*<\/i>\s*<i[^>]+>\s*<strong>/g, '');
// returned value: (String)
Lorem <i style="color:blue;"><strong>ipsum test</strong></i> dolor sit amet
html:
Lorem ipsum test dolor sit amet

Separately processing wrapped lines using jQuery

I am looking for a way to separately process the lines in a <div> that are wrapped due to a narrow width. That is, if my text is "Lorem ipsum dolor sit amet lorem \n ipsum dolor sit amet" and it is seen as below:
Lorem ipsum dolor
sit amet lorem
ipsum dolor sit
amet
Then I should be able to encapsulate each 'line' in a, say, <span> tag, such as:
<span id="line0">Lorem ipsum dolor<span>
<span id="line1">sit amet lorem</span>
... etc.
Edit: We can assume that the width and height of the div is fixed and known.
I couldn't find a proposed solution, if any exists; although there is a good suggestion for counting the lines for a fixed line-height: How to check for # of lines using jQuery
Starting with this:
<div class="narrow">Lorem ipsum dolor sit amet lorem ipsum dolor sit amet</div>
css:
.narrow {
width:60px;
}
Insert some placeholders where there are spaces:
$('.narrow').html($('.narrow').html().replace(/ /g,"<span class='count'> </span>"))
Determine the y-position of each placeholder:
$('.narrow .count') .each(function() {
var myPos = $(this).position()
alert(myPos.top)
})
From there you should be able to figure out where the start/end points of each line based on its y-position.

Categories

Resources