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
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>
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.
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.
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
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.