Indicate top heading on table of content jquery - javascript

I am making an automatic Table of Content, everything is working fine. I just need to indicated the top heading to be "bold"
jQuery(document).ready(function(){
var ToC =
"<nav role='navigation' class='table-of-contents vNav'>" +
"<ul class='vNav'>";
var newLine, el, title, link;
$(".right_section .intro_title h2, .right_section .section_one_content h2, .right_section .p_section_title h3").each(function() {
el = $(this);
title = el.text();
//link = "#" + el.attr("id");
link = "#" + el.text();
$(this).attr('id', title);
if ($( ".right_section .intro_title h2" )) {
newLine =
"<li class='vNav_heading'>" +
"<a href='" + link + "'>" +
title +
"</a>" +
"</li>";
} else {
newLine =
"<li class='vNav_lists'>" +
"<a href='" + link + "'>" +
title +
"</a>" +
"</li>";
}
ToC += newLine;
});
ToC +=
"</ul>" +
"</nav>";
$(".toc").prepend(ToC);
}); // END DOM
The code above adds a class (.vNav_lists) to all when it should detect (.right_section .intro_title h2) to have a class ".vNav_heading" and the rest should be ".vNav_lists"
This is the HTML markup, this is based on wordpress wysiwyg editor
<div class="left_section">
<div class="toc"></div>
</div>
<div class="right_section">
<div class="intro_title">
<h2>title</h2>
</div>
<p>lorem ipsum dolor</p>
<p>lorem ipsum dolor</p>
<p>lorem ipsum dolor</p>
<div class="section_one_content">
<h2>title</h2>
</div>
<p>lorem ipsum dolor</p>
<p>lorem ipsum dolor</p>
<div class="p_section_title">
<h3>title</h3>
</div>
<p>lorem ipsum dolor</p>
<p>lorem ipsum dolor</p>
<div>
This is the result, but there should be a class .vNav_heading on every top heading.
<div class="toc">
<nav role="navigation" class="table-of-contents vNav">
<ul class="vNav">
<li class="vNav_lists">class should be .vNav_heading</li>
<li class="vNav_lists">Text</li>
<li class="vNav_lists">Text</li>
<li class="vNav_lists">Text</li>
<li class="vNav_lists">class should be .vNav_heading</li>
<li class="vNav_lists">Text</li>
<li class="vNav_lists">Text</li>
</ul>
</nav>
</div>
Hope its understadable, I could not explain it well

You need to check whether parent has class "intro_title"
$(function(){
var ToC =
"<nav role='navigation' class='table-of-contents vNav'>" +
"<ul class='vNav'>";
var newLine, el, title, link;
$(".right_section .intro_title h2, .right_section .section_one_content h2, .right_section .p_section_title h3").each(function() {
el = $(this);
title = el.text();
//link = "#" + el.attr("id");
link = "#" + el.text();
$(this).attr('id', title);
if ($(this).closest(".intro_title").length) {
newLine =
"<li class='vNav_heading'>" +
"<a href='" + link + "'>" +
title +
"</a>" +
"</li>";
} else {
newLine =
"<li class='vNav_lists'>" +
"<a href='" + link + "'>" +
title +
"</a>" +
"</li>";
}
ToC += newLine;
});
ToC +=
"</ul>" +
"</nav>";
console.log(ToC);
$(".toc").prepend(ToC);
}); // END DOM
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="toc">
</div>
<div class="right_section">
<div class="intro_title">
<h2>title</h2>
</div>
<p>lorem ipsum dolor</p>
<p>lorem ipsum dolor</p>
<p>lorem ipsum dolor</p>
<div class="section_one_content">
<h2>title</h2>
</div>
<p>lorem ipsum dolor</p>
<p>lorem ipsum dolor</p>
<div class="p_section_title">
<h3>title</h3>
</div>
<p>lorem ipsum dolor</p>
<p>lorem ipsum dolor</p>
</div>

Related

Generate Table of content dynamicaly with jquery

How do I generate table of contents dynamically. following is my code but its only for one heading tag h3. I need it to work for all headings.
Here is my following sample format of the post :
<html>
<head></head>
<body>
<div id="tableofcontent"></div>
<div class="entry-content">
<h1 id="Test1">Main Heading</h1>
<p>Lorem Ipsum Lorem IpsumLorem IpsumLorem Ipsum</p>
<h2 id="Test2"> Sub Heading</h2>
<p>Lorem Ipsum Lorem IpsumLorem IpsumLorem Ipsum</p>
<h3 id ="Test3">Sub Sub Heading</h3>
<p>Lorem Ipsum Lorem IpsumLorem IpsumLorem Ipsum</p>
<h4>Sub Sub Heading</h4>
<p>Lorem Ipsum Lorem IpsumLorem IpsumLorem Ipsum</p>
</div>
</body>
</html>
How do I generate table of contents dynamically.
following is my code but its only for one heading tag h3. I need it to work for all headings.
jQuery(document).ready(function($) {
var $aWithId = $('.entry-content h3[id]');
if ($aWithId.length != 0) {
if ($aWithId.length > 0) {
$('#tableofcontent').prepend('<nav class="toc"><h3 class="widget-title">Table of Contents</h3><ol></ol></nav>');
}
}
var $aWithId = $('.entry-content h3[id]');
if ($aWithId.length != 0) {
$('.entry-content').find($aWithId).each(function() {
var $item = $(this);
var $id = $(this).attr('id');
var li = $('<li/>');
var a = $('<a/>', {
text: $item.text(),
href: '#' + $id,
title: $item.text()
});
a.appendTo(li);
$('#tableofcontent .toc ol').append(li);
});
}
});

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>

Get all headers and resursively create a tree

I want to create a tree using headers.
Example:
<h1>Wow</h1>
<h2>Blablablub</h2>
<p>Lorem Ipsum...</p>
<h1>Lalalala</h1>
<p>Lorem Ipsum...</p>
<h1>Ble</h1>
<h2>Test</h2>
<h3>Third</h3>
<p>Lorem Ipsum...</p>
This list should be created:
<ul>
<li>
<a>Wow</a>
<ul>
<li>
<a>Blablablub</a>
</li>
</ul>
</li>
<li>
<a>Lalalala</a>
</li>
<li>
<a>Ble</a>
<ul>
<li>
<a>Test</a>
<ul>
<li>
<a>Third</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>
a tags should have a custom id but that isn't important for this question. I tried to do this but I couldn't figure it out. Here's what I tried:
function find_titles(find_num, element, prefix=""){
temp_list = $("<ul></ul>");
element.find(`${prefix}h${find_num}`).each(function(i, object){
let text = $(object).text();
let id = text.replace(/[^0-9a-zA-Z]/gi, "") + random_chars();
$(object).attr("id", id);
if ($(object).next().prop("tagName").toLowerCase() == `h${find_num + 1}`){
console.log($(object));
next_titles = find_titles(find_num + 1, $(object), "+ ")[0].innerHTML;
} else {
next_titles = "";
}
$(`<li>${text}${next_titles}</li>`).appendTo(temp_list);
});
return temp_list;
}
EDIT
This:
<h1>First</h1>
<h2>Second</h2>
<p>Lorem Ipsum</p>
<h3>Third</h3>
Should be normally converted into this:
<ul>
<li>
<a>First</a>
<ul>
<li>
<a>Second</a>
</li>
</ul>
</li>
<li>
<a>Third</a>
</li>
</ul>
I don't care wether the first is a h1 h2 or a h3. In the text it's only important for styling but in the tree it isn't important.
You can first clear your data to get only heading nodes and their number and text. After that you can loop the data and build tree structure based on levels using array and index number for each level.
function tree(data) {
data = Array.from(data).reduce((r, e) => {
const number = e.nodeName.match(/\d+?/g);
if(number) r.push({ text: e.textContent, level: +number })
return r;
}, [])
const result = $("<ul>")
const levels = [
[], result
]
data.forEach(({ level, text }) => {
const li = $("<li>")
const a = $("<a>", { text, href: text })
levels[level + 1] = $('<ul>')
li.append(a)
li.append(levels[level + 1]);
levels[level].append(li)
})
return result;
}
const result = tree($("body > *"));
$("body").html(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h1>Wow</h1>
<h2>Blablablub</h2>
<p>Lorem Ipsum...</p>
<h1>Lalalala</h1>
<p>Lorem Ipsum...</p>
<h1>Ble</h1>
<h2>Test</h2>
<h3>Third</h3>
<p>Lorem Ipsum...</p>
You could also do this in one reduce method and add to tree if the element is heading.
function tree(data) {
const result = $("<ul>")
const levels = [
[], result
]
Array.from(data).reduce((r, { textContent: text, nodeName }) => {
const number = nodeName.match(/\d+?/g);
const level = number ? +number : null;
if(level) {
const li = $('<li>').append($("<a>", { text, href: text }))
r.push({ level: r[level + 1] = $('<ul>') })
r[level].append(li.append(levels[level + 1]))
}
return r;
}, levels)
return result;
}
const result = tree($("body > *"));
$("body").html(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h1>Wow</h1>
<h2>Blablablub</h2>
<p>Lorem Ipsum...</p>
<h1>Lalalala</h1>
<p>Lorem Ipsum...</p>
<h1>Ble</h1>
<h2>Test</h2>
<h3>Third</h3>
<p>Lorem Ipsum...</p>
You can iterate through all the H1 elements and then iterate through all the next header elements (all except H1). Here is an example:
const elements = $('h1').map(function() {
let container = $('<li>');
const ret = container;
container.append($('<a>').text($(this).text()));
let next = $(this).next('h2, h3, h4, h5');
while (next.length) {
const tmp = $('<li>');
tmp.append($('<a>').text(next.text()));
container.append(tmp);
container = tmp;
next = next.next('h2, h3, h4, h5');
}
return ret;
}).get();
const parent = $('<ul>');
parent.append(elements);
console.log(parent[0].innerHTML);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h1>Wow</h1>
<h2>Blablablub</h2>
<p>Lorem Ipsum...</p>
<h1>Lalalala</h1>
<p>Lorem Ipsum...</p>
<h1>Ble</h1>
<h2>Test</h2>
<h3>Third</h3>
<p>Lorem Ipsum...</p>
Using :header selector and tagName property
let $sub, $ul = $('<ul/>')
$(':header').each(function() {
let $this = $(this),
$prev = $this.prev(':header'),
$parent = $prev.length && $prev.prop('tagName') < $this.prop('tagName') ? $sub : $ul
$parent.append('<li><a>' + $this.text() + '</a></li>')
$sub = $('<ul/>').appendTo($parent.find('li:last'))
})
$('body').html($ul)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h1>Wow</h1>
<h2>Blablablub</h2>
<p>Lorem Ipsum...</p>
<h1>Lalalala</h1>
<p>Lorem Ipsum...</p>
<h1>Ble</h1>
<h2>Test</h2>
<h3>Third</h3>
<h3>Third</h3>
<p>Lorem Ipsum...</p>
<h1>First</h1>
<h2>Second</h2>
<p>Lorem Ipsum</p>
<h3>Third</h3>
<h1>First</h1>
<h2>Second</h2>
<p>Lorem Ipsum</p>
<h3>Third</h3>

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

Click to get index of array element with jquery

Ok, I've tried my best at searching, but.
I've got a task, where I need to load some js using the Ajax and so on. Long story short, I've stuck.
First a code in script.js (which I must load AND which I can't modify):
var divs = [
'<div class="item">Lorem ipsum 0</div>',
'<div class="item">Lorem ipsum 1</div>',
'<div class="item">Lorem ipsum 2</div>',
'<div class="item">Lorem ipsum 3</div>',
'<div class="item">Lorem ipsum 4</div>',
'<div class="item">Lorem ipsum 5</div>',
'<div class="item">Lorem ipsum 6</div>',
'<div class="item">Lorem ipsum 7</div>'
];
delete(divs[3]);
Then my script to load it
$.getScript('script.js', function() {
$('.a').append('<div class="yep">' + divs.join('') + '</div>');
$('.item').each(function() {
$(this).click(function() {
console.log( $('.item').index(this) );
});
});
});
The problem is that on click I need to get index of item in array, i.e. if I click on "Lorem ipsum 4" console should print "4", not "3" as it happens now (because of deleted element which doesn't appear in dom).
Is there a way to get the right result using jQuery?
Ok, I need to say that it's a task. And here is the thing: I simply CAN'T modify script.js. Let's say it's on server and I have no access to it until I get it. But I need index of an element which it has in the original array.
You're asking for the INDEX of the clicked item. Your code is doing exactly what it's supposed to do. jQuery has no way to know if you've deleted items from the original list, it can only see what's currently there.
The best solution is to add an HTML attribute to the original items, and console.log that attribute instead of the .index. Like this:
var divs = [
'<div data-idx="0" class="item">Lorem ipsum 0</div>',
'<div data-idx="1" class="item">Lorem ipsum 1</div>',
'<div data-idx="2" class="item">Lorem ipsum 2</div>',
'<div data-idx="3" class="item">Lorem ipsum 3</div>',
'<div data-idx="4" class="item">Lorem ipsum 4</div>',
'<div data-idx="5" class="item">Lorem ipsum 5</div>',
'<div data-idx="6" class="item">Lorem ipsum 6</div>',
'<div data-idx="7" class="item">Lorem ipsum 7</div>'
];
delete(divs[3]);
$('.a').append('<div class="yep">' + divs.join('') + '</div>');
$('.item').each(function() {
$(this).click(function() {
console.log($(this).data('idx'));
});
});​
http://jsfiddle.net/mblase75/8NLGm/
Try something like this:
http://jsfiddle.net/YjNAL/1/
var divs = [
'<div class="item">Lorem ipsum 0</div>',
'<div class="item">Lorem ipsum 1</div>',
'<div class="item">Lorem ipsum 2</div>',
'<div class="item">Lorem ipsum 3</div>',
'<div class="item">Lorem ipsum 4</div>',
'<div class="item">Lorem ipsum 5</div>',
'<div class="item">Lorem ipsum 6</div>',
'<div class="item">Lorem ipsum 7</div>'
];
delete(divs[3]);
var yep = $('<div class="yep"></div>'); // Changed (from edit)
for (var i = 0; i < divs.length; i++) {
if (divs[i]) { // Don't operate on undefined items
var theDiv = $(divs[i]).data("idx", i); // Changed (from edit)
yep.append(theDiv); // Changed (from edit)
}
}
$(".a").append(yep); // Changed (from edit)
$('.item').on("click", function() {
console.log( $(this).data("idx") );
});
Notice how the original array isn't modified.
Each item in the array is modified and creates a jQuery object before it is appended. <- I'm sure that part could be done more efficiently, I was just trying to throw something together.
It stores its index in the array from of the for loop, so that should be accurate.
Any undefined (deleted) items are ignored.
You can try this. Give all the divs an ID, and get that:
var divs = [
'<div class="item" id="0">Lorem ipsum 0</div>',
'<div class="item" id="1">Lorem ipsum 1</div>',
'<div class="item" id="2">Lorem ipsum 2</div>',
'<div class="item" id="3">Lorem ipsum 3</div>',
'<div class="item" id="4">Lorem ipsum 4</div>',
'<div class="item" id="5">Lorem ipsum 5</div>',
'<div class="item" id="6">Lorem ipsum 6</div>',
'<div class="item" id="7">Lorem ipsum 7</div>'
];
delete(divs[3]);
$('.a').append('<div class="yep">' + divs.join('') + '</div>');
$('.item').each(function() {
$(this).click(function() {
console.log(this.id);
});
}); ​
Numbers in id's?!?
Yea, I know, in HTML4 ids starting with numbers weren't allowed. HTML5, however, removed this restriction.
Those divs will validate according to the w3 validator.
If you would rather have it a bit dynamic, you could create an element, add a data field to it, then access that element:
var divs = [
'<div class="item">Lorem ipsum 0</div>',
'<div class="item">Lorem ipsum 1</div>',
'<div class="item">Lorem ipsum 2</div>',
'<div class="item">Lorem ipsum 3</div>',
'<div class="item">Lorem ipsum 4</div>',
'<div class="item">Lorem ipsum 5</div>',
'<div class="item">Lorem ipsum 6</div>',
'<div class="item">Lorem ipsum 7</div>'
];
var newdivs = $('<div class="yep">').append(divs.join(""));
newdivs.find('.item').each(function() {
$(this).data("myindex", $(this).index());
});
var elementToDelete = 3
delete(divs[elementToDelete]);
newdivs.find('.item').eq(elementToDelete).remove();
$('.a').append(newdivs);
$('.a').on('click','.item',function() {
$(this).css("background-color","lime");
alert($(this).data("myindex"));
});
see how it works here:
http://jsfiddle.net/rZjNd/2/

Categories

Resources