JavaScript: How to generate nested ordered list - javascript

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.

Related

Javascript hiding objects in loop

Please help me. I want to hide ext by clicking on link, but something is wrong
But I could write only this, and don't undertand why it's not working! Maybe there is another way to do it? It's every time fills the same function. to li1 li2func, therefore thereis li1
Link to jsfiddler
html:`
<div>
<div class="left">
<ul>
<li><a id="11" href="#">one</a></li>
<li><a id="12" href="#">two</a></li>
<li><a id="13" href="#">three</a></li>
</ul>
</div>
<div class="right">
<p id="1">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Doloremque, qui.
</p>
<p id="2">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repellendus reiciendis veritatis voluptatibus optio explicabo? Dignissimos ex amet mollitia doloribus a.
</p>
<p id="3">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Impedit porro quisquam harum nemo, vitae itaque.</p>
</div>
</div>`
JS code:
var el = document.getElementsByTagName("p");
var cs = document.getElementsByTagName("a");
if(el) {
for (i = 0; i < el.length; i++) {
td = cs[i];
(function (_td) {
td.addEventListener('click', function(){
console.log(_td);
console.log(i);
document.getElementsByTagName("div")[i].className += document.getElementsByTagName("div")[i].className ? ' invis' : 'invis';
});
})(td);
You have a few problems with your javascript. You should have noticed this error in your javascript console: "Uncaught TypeError: Cannot read property 'className' of undefined", which might have led you to discover that document.getElementsByTagName("div") is not the selector you should be using. You did the work to produce an array of p tags (inexplicably named el), but then didn't make any reference to that within the closure you built.
Here's one way to fix that problem: give your closure a second argument, and pass the p tag of the corresponding number (i) to the a tag to which you're binding the click handler. Then modify the className string of that element.
var el = document.getElementsByTagName("p");
var cs = document.getElementsByTagName("a");
if(el) {
for (i = 0; i < el.length; i++) {
td = cs[i];
ptag = el[i];
(function (_td,_el) {
td.addEventListener('click', function(){
console.log(_td);
console.log(i);
_el.className += _el.className ? ' invis' : 'invis';
});
})(td,ptag);
}
}
One other thing: you'll notice that console.log(i) always produces 3 because that i is not bound to the scope of the click handler, but rather the outer scope of the for loop, so when the user clicks on one of the a tags, the loop has already completed and i will always equal 3.
While you tagged jquery this is how can you do with jquery by using the index() of href's closest li
$('ul > li > a').on('click' , function(){
var ThisId = $(this).closest('li').index();
$('div.right > p:eq('+ThisId+')').slideToggle(100);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<div class="left">
<ul>
<li><a id="11" href="#">one</a></li>
<li><a id="12" href="#">two</a></li>
<li><a id="13" href="#">three</a></li>
</ul>
</div>
<div class="right">
<p id="1">
1 Lorem ipsum dolor sit amet, consectetur adipisicing elit. Doloremque, qui.
</p>
<p id="2">
2 Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repellendus reiciendis veritatis voluptatibus optio explicabo? Dignissimos ex amet mollitia doloribus a.
</p>
<p id="3">3 Lorem ipsum dolor sit amet, consectetur adipisicing elit. Impedit porro quisquam harum nemo, vitae itaque.</p>
</div>
</div>
document.getElementsByTagName will never return null, so if(el) in your code is unnecessary. An empty array will always evaluate to true. If you want i to not equal 3, then you have to add it to the closure at the moment it is 0, 1, and 2. I called it _index here:
var paragraphs = document.getElementsByTagName("p");
var links = document.getElementsByTagName("a");
for (i = 0; i < paragraphs.length; i++) {
link = links[i];
paragraph = paragraphs[i];
(function (_link,_paragraph, _index) {
_link.addEventListener('click', function(){
console.log(_link);
console.log(_index);
_paragraph.classList.toggle('invis');
});
})(link,paragraph, i);
}
Here is a cleaner version
function init(link, paragraph, index) {
link.addEventListener('click', function(){
paragraph.classList.toggle('invis');
});
}
var paragraphs = document.getElementsByTagName("p");
var links = document.getElementsByTagName("a");
for (i = 0; i < paragraphs.length; i++) {
init(links[i], paragraphs[i], i);
}

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 to show child element when other child element is overflowing with JQuery

I have recently been working on a comment feature. By default, the height of the paragraph element containing the text is 80px. Overflow is set to hidden.
I have another button (labelled "See More") that expands the paragraph by changing height to 'auto'. This button should only be visible if the paragraph content is overflowing the default 80px height. Otherwise the button must not be displayed.
I have tried to do this with a javascript for loop and some JQuery code, though it doesn't work as it should. It shows or hides the button for all comment sections.
Here is the html:
<div class="commentOwnerPost">
<div class="commentPostHeader">
<h4 class="commentOwnerName">NavyFoxKid</h4>
<h4 class="commentPostDate">3 days ago</h4>
</div>
<p class="commentText"> lorem ipsum dolor sit amet consectur lorem ipsum dolor sit amet consectur
amet consectur lorem ipsum dolor sit amet consectur lorem ipsum
</p>
<div class="commentPostFooter">
<a class="btnReply">Reply</a>
<a class="btnSeeMore">See More</a>
</div>
</div>
Here is the JavaScript:
$(document).ready(function(){
var element = $('.commentOwnerPost');
for(i=0; i < element.length; i++){
var commentText = $(element[i]).children('.commentText');
if ($(commentText).offsetHeight < $(commentText).scrollHeight) {
$parent = $(commentText).parent('.commentOwnerPost');
$parent.find('.btnSeeMore').hide();
console.log('Comment text not overflowing ');
} else {
$parent = $(commentText).parent('.commentOwnerPost');
$parent.find('.btnSeeMore').show();
console.log('Comment text overflowing ');
}
$('.btnSeeMore').click(function(){
});
}
});
Thanks for taking the time to read. Any help would be appreciated.
It works perfectly for me, I simplify your code:
$(document).ready(function(){
var elements = $('.commentOwnerPost');
elements.each(function() {
var el = $(this).find('.commentText').get(0);
if(el.offsetHeight < el.scrollHeight) {
$(this).find('.btnSeeMore').show();
} else {
$(this).find('.btnSeeMore').hide();
}
});
});
.commentText { max-height: 25px; overflow:hidden;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="commentOwnerPost">
<div class="commentPostHeader">
<h4 class="commentOwnerName">NavyFoxKid</h4>
<h4 class="commentPostDate">3 days ago</h4>
</div>
<p class="commentText"> lorem ipsum dolor sit amet consectur lorem ipsum dolor sit amet consectur
amet consectur lorem ipsum dolor sit amet consectur lorem ipsum
</p>
<div class="commentPostFooter">
<a class="btnReply">Reply</a>
<a class="btnSeeMore">See More</a>
</div>
</div>
<div class="commentOwnerPost">
<div class="commentPostHeader">
<h4 class="commentOwnerName">NavyFoxKid</h4>
<h4 class="commentPostDate">3 days ago</h4>
</div>
<p class="commentText"> lorem ipsum dolor sit amet.
</p>
<div class="commentPostFooter">
<a class="btnReply">Reply</a>
<a class="btnSeeMore">See More</a>
</div>
</div>

Manipulate html-string variable with 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

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