Dynamically add ID to header based on header name? - javascript

I'm using Jquery for a small project and I have run into an issue.
I'm trying to create a small script that will look at a page, and automatically assign an ID to H1 headers based on the header text, i.e:
<h1> This is a Test </h1>
to
<h1 id="This_is_a_Test"> This is a Test</h1>
I've been able to select every H1, but I haven't been able to modify the ID based on the header text.
var $headers = $('.body h1);
$headers.attr("id", not sure exactly how to pass headers text to this?);
I'm obviously very much a novice, but would appreciate some help in this!
Thanks!

If you want to do this for every h1 element you have in your page you can do something like that:
$(function() {
$('h1').each(function() {
$(this).attr('id', $(this).text().replace(/ /g, '_'))
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>Some text</h1>
<h1>Some other text</h1>
The function will take every h1 element, for each it will set the value of the id attribute (using the attr function) to the text() of that h1 element (while changing every space with underscore).

Use a jQuery's .attr() with the callback option to read the text of the headers, converting it to snake case, and setting it as the id:
var $headers = $('.body h1');
$headers.attr("id", function() {
return $(this)
.text() // get the h1 text
.trim() // remove spaces from start and the end
.toLowerCase() // optional
.replace(/\s/g, '_'); // convert all spaces to underscores
});
console.log($headers[0]);
console.log($headers[1]);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="body">
<h1>This is a Test </h1>
<h1>This is a another Test </h1>
</div>

First you need to generate the id by using the text method for h1.
var $headers = $('.body h1'); // select the element
var text = $headers.text(); // get the text of the element
var id = text.trim().split(' ').join('_'); // split by space and join using _
$headers.attr("id", id);

Related

How do I get a string that's the text() of an element, but with spaces added after divs?

JSFiddle here
Hi! I'm trying to output a string from the .contents().text() of an element... but with spaces between the content of each div (without changing the actual DOM).
HTML:
<!-- I don't have control over how many divs are in .myTextArea, or what text. It's really dynamic. There are also lists, etc.--tons of different types of elements. -->
<div class="myTextArea">
<div>Hey there!</div><div>I like turtles.</div><div>Do you like them?</div>
</div>
jQuery:
var myTextDescription = $(".myTextArea").contents().text();
console.log(myTextDescription);
Currently, it outputs:
Hey there!I like turtles.Do you like them?
...and this is what I want it to output: The same thing, but with spaces after the content of each div:
Hey there! I like turtles. Do you like them?
Note: Other answers on SO make you change the actual DOM (AKA, they add actual spaces after the elements on the page), and then they just grab the text() string. I don't want to change the DOM.
Also, I can't use .html() instead and try to strip away stuff, because there will be wayyyyyy too many types of elements to worry about.
JSFiddle here
You're almost there. Replace .text() with:
//get text content of all nodes
.map((i,d) => d.textContent).get()
//remove white space
.filter(t => !!t.trim())
//join the text from all nodes with a space
.join(' ');
Check out the demo below:
var myTextDescription = $(".myTextArea").contents().map((i,d) => d.textContent).get().filter(t => !!t.trim()).join(' ');
console.log(myTextDescription);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="myTextArea">
<div>Hey there!</div><div>I like turtles.</div><div>Do you like them?</div>
</div>
In case you needed to exclude text in a div, say with a class exclude you can use the :not() psedo selector like so:
... .contents(':not(".exclude")') ....
..as in the demo below:
var myTextDescription = $(".myTextArea").contents(':not(".exclude")').map((i,d) => d.textContent).get().filter(t => !!t.trim()).join(' ');
console.log(myTextDescription);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="myTextArea">
<div>Hey there!</div><div class="exclude">Please exclude this!</div><div>I like turtles.</div><div>Do you like them?</div><div class="exclude">Please exclude this too!</div>
</div>
One way to solve this is to use JavaScript's querySelectorAll method to return a list of all the DIVs in your myTextArea element. You can then run through each element in the list, get its innerText and place a space after each one:
var myTexts = document.querySelectorAll('.myTextArea > div');
var show = document.querySelector('#show');
var output = "";
for (var x = 0; x < myTexts.length; x++) {
if (output == "") { // Skip adding space before first string
output = myTexts[x].innerText;
} else { // Add space before each appended string
output += " " + myTexts[x].innerText;
}
}
show.innerText = output;
<div class="myTextArea">
<div>Hey there!</div>
<div>I like turtles.</div>
<div>Do you like them?</div>
</div>
<div id="show"></div>

jQuery: How to select text between two closed html tags

I am trying to wrap the intro/help text in html document using jQuery.It is not inside any tag but between two closed html tags.
Please see attached code snippet for example. the 2nd end tag can also be other than <p>.
var txtHelp = jQuery('b.page-title').nextUntil('p').text();
console.log(txtHelp);
//jQuery('b.page-title').nextUntil('p').text().wrap("<p />");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<b class="page-title"><h4>System Log</h4><hr class="text-primary"></b>
How to select this text and wrap it in new P-Tag
<p align="center">This can by any html tag</p>
The nextUntil() method not selects textnodes.
You can get the text node by nextSibling property of node and get text content by textContent property of text node.
var txtHelp = jQuery('b.page-title')[0] // get the dom object
.nextSibling // get the text node next to it
.textContent; // get text content
console.log(txtHelp);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<b class="page-title"><h4>System Log</h4><hr class="text-primary"></b>
How to select this text and wrap it in new P-Tag
<p align="center">This can by any html tag</p>
UPDATE 1 : If you want to wrap the element by a p tag then do it like.
$( // wrap by jquery to convert to jQuery object
$('b.page-title')[0] // get the dom element also you can use `get(0)`
.nextSibling // get the textnode which is next to it
).wrap('<p/>'); // wrap the element by p tag
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<b class="page-title"><h4>System Log</h4><hr class="text-primary"></b>
How to select this text and wrap it in new P-Tag
<p align="center">This can by any html tag</p>
UPDATE 2 : If it contains br tag and you want to include it as a text then do something tricky using contents() method.
var get = false;
$($('b.page-title')
.parent() // get it's parent
.contents() // get all children node including text node
.filter(function() {
if ($(this).is('b.page-title')) {
get = true; // if element is 'b.page-title' then set flag true , we need to get element from here
return false // return false that we don't need the 'b.page-title'
}
if ($(this).is('p')) // check element is `p`, that we need to get element uptop tag
get = false; // update flag
return get; // return the flag for filtering
})).wrapAll('<p/>'); // use wrapAll to wrap all elements withing single tag
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<b class="page-title"><h4>System Log</h4><hr class="text-primary"></b>
How to select this text
<br/>and wrap it in new P-Tag
<p align="center">This can by any html tag</p>
For a pure jQuery approach, you can try this:
var contents = $('b.page-title').contents(),
textNodes = contents.filter(function() { return this.nodeType === 3; });
console.log(textNodes[0].textContent);
See contents()

If element contains any of these words then wrap that word in span

I'd like to highlight certain words if they're found within a title by wrapping those words in a span class. Is there a way of writing a list of words to check for and then using the same command to wrap any of those words with the span class?
For example:
<h1>This title contains the word Balloon</h1>
<h1>This title contains the word Oranges</h1>
<h1>This title doesn't contain either of those terms</h1>
Something to check each of those three elements (but there would be 20 or so) for the words Balloon or Oranges and then wrap just those words in a span color.
I could use:
$('h1').html($('h1').html().replace(/(balloon)/g,'<span class="red">balloon</span>'));
but I'd have to write that query for every word, whereas I'd like something a little simpler, if possible.
You can keep an array of words, then iterate and replace the words in each header
var words = [
"balloon",
"either"
];
$('h1').html(function(_,html) {
var reg = new RegExp('('+words.join('|')+')','gi');
return html.replace(reg, '<span class="red">$1</span>', html)
});
.red {color : red}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>This title contains the word Balloon</h1>
<h1>This title contains the word Oranges</h1>
<h1>This title doesn't contain either of those terms</h1>
Loop through your element set, like so, and replace it with anything in an array of keywords:
<h1>This title contains the word Balloon</h1>
<h1>This title contains the word Oranges</h1>
<h1>This title doesn't contain either of those terms</h1>
<h1>This title contains both Balloon and Oranges</h1>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script>
var wordsToHighlight = ['Balloon', 'Oranges'];
$('h1').each(function() {
for (var i = 0, l = wordsToHighlight.length; i < l; i++) {
if ($(this).html().indexOf(wordsToHighlight[i]) !== -1) {
var newValue = $(this).html().replace(wordsToHighlight[i],
'<span>' + wordsToHighlight[i] + '</span>');
$(this).html(newValue);
}
}
});
</script>
While the answers provided here are theoretically correct, they will use innerHTML which is evil. It will destroy events and triggers DOM generation over and over again. Also, you'll probably need to remove highlights at a time, so you are reinverting the wheel.
There's a plugin that solves your problem: mark.js
Usage can be done e.g. the following way:
$(function(){
// pass an array to highlight or a simple string term
$("h1").mark(["Balloon", "Oranges"]);
});
mark{
background: orange;
color: black;
}
<script src="https://cdn.jsdelivr.net/jquery/3.1.0/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/mark.js/7.0.0/jquery.mark.min.js"></script>
<h1>This title contains the word Balloon</h1>
<h1>This title contains the word Oranges</h1>
<h1>This title doesn't contain either of those terms</h1>
You can have an object containing all the words you want wrapped (including another specifications eg class or the tag used) and then just iterate over all fields of the object while using your command that just constructs the string to be used.

jQuery get text in element but ignore style tags

I have javascript code that tests if a div has some non whitespace text content.
This works but it cannot differentiate between text content or style tag declarations within the div and the test fails when there is no text content but a style tag with css data.
Question is how to test for empty text content while ignoring the style tag?
HTML:
<div id='test1'>
<div> </div>
</div>
<div id='test2'>
<div> </div>
<style>
.style1{
border:1px;
}
</style>
</div>
Javascript:
// Works
if($('#test1').text().trim().length==0){
alert('test1 has no content!');
}
// Does not work due to style1 declaration within style tag within div
if($('#test2').text().trim().length==0){
alert('test2 has no content!');
}
Test URL:
http://jsfiddle.net/sLDWB/
One option is cloning the element and removing the style tags:
$.fn.isTextless = function() {
var txt = this.first()
.clone()
.find('style')
.remove()
.end()
.text();
return $.trim(txt).length === 0;
}
if ( $('#test2').isTextless() ) {
alert('test2 has no content!');
}
http://jsfiddle.net/5Z3M4/
You can just use a common class for all the elements you need to check, loop through them using each, store the initial HTML, remove the style tag, do your check and restore the initial HTML. Like so :
$('.testdiv').each(function() {
var divHtml = $(this).html();
$(this).find('style').remove();
if($(this).text().trim().length==0){
alert( $(this).attr('id') + ' has no content!');
}
$(this).html(divHtml);
});
http://jsfiddle.net/sLDWB/1/
Subtract the style tag's length with the actual length.
Try,
if($('#test1').text().trim().length==0){
alert('test1 has no content!');
}
if($('#test2').text().trim().length - $('#test2 style').text().trim().length ==0){
alert('test2 has no content!');
}
DEMO
Use following javascript code
document.getElementById('test2').innerText
The style tag is meant to go in the head of a page. Why do you even have .style1 there if no element uses style1? If you want to change the style of a div, either do <div style="border: 1px;"> or make a style declaration in the <head> part of the HTML page.
In short, you shouldn't ever have a <style> tag outside of <head>.

JavaScript document.execCommand remove formatBlock formatting?

If I format a piece of text on a page like this:
document.execCommand('formatBlock', false, 'h1');
What do I do to remove this formatting?
I suppose document.execCommand('removeFormat',false,false) would do it?
Issuing document.execCommand('formatBlock', false, 'div') on the <h1>-block will remove the <h1>-tag and replace it with a <div>-tag 1. Would that be viable?
1 If you're not using IE that is
I clear the effect of h1 using this:
document.execCommand('formatBlock', false, 'p');
You have changed its format to h1, so we can change it back to normal paragraph format in the same way.
If your put each paragraph in a <div> , you can also use this:
document.execCommand('formatBlock', false, 'div');
to set the format to the same as other blocks.
I had the same problem where I need to delete the h1 tag wrapping my text.
What I did was get the parent node of the selected text:
var elem_parent_node =
window.getSelection().getRangeAt(0).startContainer.parentNode;
And then check if it's nodeName is "H1"; if yes, then store the selected text in a selected_text variable and then delete the node itself:
elem_parent_node.remove();
Then,
document.execCommand('insertText', false, select_text);
To replace the h1 with its text content node, then use the code below.
For a complete code, you would need to add some checks to make sure you remove what you want.
const button = document.getElementsByTagName('button')[0]
button.addEventListener('click', event => {
const selection = window.getSelection()
if (!selection.isCollapsed) {
selection.anchorNode.parentNode.replaceWith(selection.anchorNode)
}
})
<div contenteditable="true">
<p>Some text</p>
<h1>Select me then click the button</h1>
<p>Some text</p>
</div>
<button>Click to remove H1</button>
you may have to find the parent tag, then use innerHTML to get the text and replace the original data between the parent tag and end-tag with the innerHTML. This would however remove all formatting.
Seems that you cannot "undo" a formatBlock. You can always replace one "formatBlock" with some other "formatBlock" as long as it is another block level HTML tag.
Proposed solutions above are feasible, although they don't proof against the parent beeing the conteneditable-container (div?), so this could potentially remove/destroy the writable area.
A bit safer workaround for this issue may be to replace the "formatBlock" with a less used one, as f.ex. <address> and then remove this <address> with a regexp-replacer:
function Header() { //formatBlock: "h1"-"h6", "p", "pre", "div", "dd", "dt" or unformat it(=empty) - except using "address" (⇓see⇓)
var Fo = prompt('Please enter: h1-h6(=number), p, pre, ...','1');
if (Number(Fo)) {
Fo = 'h' + Fo; //format with h1-h6 directly by entering numbers - or p, pre, etc...
document.execCommand('formatblock', false, Fo);
} else if (Fo=='') { //if let empty, then unformat the block:
document.execCommand('formatBlock', false, 'address'); //using "address" as a temporary substitute - hoping that it's not used anywhere else in the document...
let inner = document.getElementById('content').innerHTML; //internal code of document
inner = inner.replace(/<address>/g,'<br>').replace(/n\n\n|\n\n|\n\s/g,'\n'); //Regex deleting (all) <address>-headings (and spaces) from document, replacing them with a line-break
document.getElementById('content').innerHTML = inner; //restore the content with replaced paragraph's
} else { document.execCommand('formatblock', false, Fo) }
}

Categories