Targeting first letter of each word in h1 using javascript - javascript

I am trying to wrap the first letter of each word in my heading tags with a span class so that I can style them using CSS. I have tried to use a snippet I've found on here, but I have 2 h1 tags and it is taking the first one and repeating it for the second!
The function is this:
<script>
$(document).ready(function() {
var words = $('h1').text().split(' ');
var html = '';
$.each(words, function() {
html += '<span class="firstLetter">' + this.substring(0, 1) + '</span>' + this.substring(1) + ' ';
$('h1').html(html);
});
});
</script>
So I have an h1 in the banner at the top, and another one at the start of the content, but the function is taking the top banner heading and replacing the content heading with it, but the span class is working!
I know you shouldn't have 2 h1s, but I want to target all headings anyway, and its a CMS for a client so I can't guarantee they won't use multiple h1 going forwards, so I am testing it out!

Recursively loop over the text nodes inside the headings and then wrap the words within a span and replace the text node with a container span that holds all the wrapped words.
Then style the first letter of these spans using the ::first-letter CSS pseudo element.
NOTE:
Directly replacing the innerHTML of the headings might cause bugs if the headings have elements in them.
For ex: In the snippet below, the headings have some elements within them. The first one has an anchor element and the second one has an svg and these are common use cases. But if you directly replace the innerHTML of these headings that would eliminate these elements inside them, which is not desired. So, it's essential that you only wrap the text nodes within a span.
::first-letter only works with block-level elements, so you need to set the display property of spans to inline-block.
const headingEls = document.querySelectorAll("h1");
function wrapWithSpan(node) {
if (node.nodeName === "#text") {
const containerSpan = document.createElement("span");
containerSpan.innerHTML = node.textContent
.split(" ")
.map((word) => `<span class="word-span">${word}</span>`)
.join(" ");
node.parentNode.replaceChild(containerSpan, node);
} else {
Array.from(node.childNodes).forEach(wrapWithSpan);
}
}
headingEls.forEach(wrapWithSpan);
a[href^="#"] {
display: inline-block;
text-decoration: none;
color: #000;
}
a[href^="#"]:hover,
a[href^="#"]:hover *,
a[href^="#"]:focus,
a[href^="#"]:focus * {
text-decoration: underline;
}
a[href^="#"]:hover::after,
a[href^="#"]:focus::after {
color: #aaa;
content: "#";
margin-left: 0.25rem;
font-size: 0.75em;
}
h1 .word-span {
display: inline-block;
}
h1 .word-span::first-letter {
color: palevioletred;
text-transform: uppercase;
}
<h1 id="heading">Heading with link</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
<h1>Heading with SVG <svg height="20" width="20"><circle cx="10" cy="10" r="10" fill="green" /></svg></h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
If you just want to style the first letter of each heading, you don't need JS, you can do it by using only the ::first-letter CSS pseudo element.
a[href^="#"] {
text-decoration: none;
color: #000;
}
a[href^="#"]:hover,
a[href^="#"]:focus {
text-decoration: underline;
}
a[href^="#"]:hover::after,
a[href^="#"]:focus::after {
color: #aaa;
content: "#";
margin-left: 0.25rem;
font-size: 0.75em;
}
h1::first-letter {
color: palevioletred;
text-transform: uppercase;
}
<h1 id="heading">Heading with link</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
<h1>Heading with SVG <svg height="20" width="20"><circle cx="10" cy="10" r="10" fill="green" /></svg></h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>

const h1 = document.getElementsByTagName("H1");
for (const item of h1) item.innerHTML = item.innerText.split(" ").map(word => "<span style='color: red !important;'>" + word[0] + "</span>" + word.slice(1)).join(" ")
Assuming that the your h1 tags have no children. Its easy to modify this to get the correct result though.

As per OP's request, this will "wrap the first letter of each word".
Since there are two <h1> elements (as OP said, very wrong), one should iterate them using each too, same way OP did with the words array.
$(document).ready(function() {
$('h1').each( function(index, heading) {
const words = $(heading).text().split(' ')
let html = '';
$.each(words, function() {
html += '<span class="firstLetter">'+this.substring(0,1)+'</span>'+this.substring(1) + ' ';
})
$(heading).html(html);
})
});
span.firstLetter {
color: violet;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h1>Lorem ipsum dolor sit amet</h1>
<hr>
<h1>Bacon ipsum dolor amet biltong pork chop bacon</h1>

This should work:
.firstLetter
{
color:red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h1>Hello World</h1>
<h1>Goodbye World</h1>
<script>
var h1s = document.getElementsByTagName('h1');
for(const h1 of h1s)
{
let text = h1.innerText.split(' ');
let html = "";
for(const word of text)
{
html += '<span class="firstLetter">' + word.substring(0, 1) + '</span>' + word.substring(1) + ' ';
}
h1.innerHTML = html;
}
</script>

To style each first letter of every word in every heading, you can use jQuery's .html() (as you're using jQuery in your question). This will loop over each h1, and replace the text within it with the returned value. The new value replaces the first letter of each word (referred to using $& in the replacement argument) matched using the regular expression \b\w (which matches a word boundary (\b) followed by a character (\w)) with the character itself wrapped in <span> tags:
$("h1").html(function(i, txt) {
return txt.replace(/\b\w/g, '<span class="firstLetter">$&</span>');
});
.firstLetter {
color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h1>This is a heading</h1>
<h1>This is another heading</h1>

Related

Highlighting text based on its match with another string

I'm trying to highlight text based on whether it's matched with a string recieved from a database or not. To show the problem, here's an example.
Given this text:
<div class='text'>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</div>
And this text:
<div id='matchingHighlight'>
Lorem ipsum <!-- Highlight this text within <div id='text'></div> !-->
</div>
How would I highlight the text within .text that matches with the text from #matchingHighlight (Lorem ipsum)?
To 'highlight' the keyword / phrases, you will need to wrap it around any inline element. I'm suggesting you use <span> tag.
The following logic will replace the text with the same text wrapped inside a span tag with a class of .highlight
$(document).ready(function() {
var $text = $('.text');
var textToHighlight = $('#matchingHighlight').text().trim();
var textCurrent = $text.html().trim();
var isTextExists = textCurrent.indexOf(textToHighlight) > -1;
if(isTextExists) {
textCurrent = textCurrent.replace(textToHighlight, "<span class='highlight'>" + textToHighlight + "</span>");
$text.html(textCurrent);
}
});
You can then style it using css, for example
.highlight {
background: yellow;
}
https://pastebin.com/vaB3wa96

Truncate paragraphs by line not characters

I have multiple items in a div with paragraphs and I would like to truncate them to 2 lines. I have tried to truncate using the height but it results in cut off words. I can't use characters because in some cases the words are long and get pushed to a new line.
I am trying to work with getClientRects() as you'll see in the fiddle below.
Also note that I can't use any plugins for the project I am working on.
I found this example on another post: Working Truncate from stackoverflow post
My Fiddle:
JS Fiddle
var lines = $(".truncate")[0].getClientRects();
var divHeight = 0;
for (var i=0, max = 2; i < max; i++)
divHeight += lines[i].bottom - lines[i].top;
divHeight += i;
$(".truncate").height(divHeight);
There's a number of issues.
The code you're trying to work from takes advantage of a quirk related to display: inline but you don't set display: inline, instead leaving .truncate at the browser default of display: block.
ready isn't a real event and jQuery no longer fakes it when using .on('ready', ...) so your code never runs.
jQuery's .height() requires that the argument be in the form of a CSS height value. This means you need to use something that results in, for example, '50px' rather than just 50.
height is ignored on inline elements so it'll have to be set on the outer element. The code you were working from did this but you didn't follow it.
Your code assumes that the number of lines will always be two or more.
overflow: hidden isn't set so the text itself will push outside its container even if the container was shortened.
All together, your code should look something like this instead:
.item {
width: 400px;
margin: 20px;
display: inline-block;
overflow: hidden;
box-sizing: content-box;
}
.truncate {
display:inline;
}
$(document).ready( function(){
var lines = $(".truncate")[0].getClientRects();
var divHeight = 0;
var max = lines.length >= 2 ? 2 : lines.length;
for (var i=0; i < max; i++) {
divHeight += lines[i].bottom - lines[i].top;
}
divHeight += i;
$(".item").height(divHeight + 'px');
});
JSFiddle
Using the css answer from css-tricks (https://css-tricks.com/line-clampin/) assuming you know the line-height.
.item {
width: 400px;
margin: 20px;
overflow: hidden;
}
.fade {
position: relative;
height: 2.4em; /* exactly two lines */
}
<div class="item fade">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</div>
<div class="item fade">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</div>

Javascript word highlighting in a text

I have a variable called output in Javascript, which has the following content.
var output = "something."
I want to search and highlight the words "word1" and "word2" in them when I display the content of output. The above content is dynamic. Assuming I have a variable output which has the text and an array called arr1 which has the elements to be searched and highlighted, how can I display the entire content with words highlighted in javascript?Please let me know. Thanks in advance.
Assuming you want literal phrases that are generally just words (no special characters or punctuation), you could use a regular expression similar to this (I've also added case insensitivity):
var output = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
var arr1 = ['eiusmod tempor', 'consectetur'];
var regex = new RegExp('('+arr1.join('|')+')', 'gi');
output = output.replace(regex, "<b>$1</b>");
// the following line is for debug purposes only. I've added it
// to better display what's happening just for the Stackoverflow
// code snippet editor.
document.body.innerHTML = output;
b {
background: yellow;
font-weight: normal;
}
Edit:
Quick edit to include logic that ensures "consectrtur" is matched, but "consecteturs" is not. It just needs a simple look ahead of (?!\\w) and a "look behind" (not a real look behind because javascript doesn't support it) of (\\W+|^) ensuring the matched term is not surrounded by word characters, and thus not part of a different word.
var output = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse consecteturs cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum";
var arr1 = ['eiusmod tempor', 'consectetur', 'orem', 'laborum'];
var regex = new RegExp('(\\W+|^)('+arr1.join('|')+')(?!\\w)', 'gi');
output = output.replace(regex, "$1<b>$2</b>");
// the following line is for debug purposes only. I've added it
// to better display what's happening just for the Stackoverflow
// code snippet editor.
document.body.innerHTML = output;
b {
background: yellow;
font-weight: normal;
}
You can wrap the words you want highlighted with a <span> tag then add a class or inline styling to the tag for your highlight color. This would require that you identify the words you want highlighted and add the opening tag before and closing tag after with a String.replace(word, tag + word + closing_tag) or something similar.
To wrap something in HTML using Javascript, you should be looking at Regex. For example:
str = str.replace(/(neoplasm)/ig, "<b>$1</b>");
To replace from an array, you could do something like connecting your strings with a join using the regex OR: | to generate a custom regex string, matching those words.
EDIT: Like Joseph just suggested, just one minut before me.

CSS or JavaScript putting background color into only part of a paragraph

Here is what I have so far and here is the link to see it online.
p { line-height: 2; }
.highlight { background-color: yellow; }
The goal is to wrap the quoted content like below (no border is necessary but I want the whole background including the space between lines to be colored)
I see that the picture isn't clear but I hope you can figure out the idea.
The current version only highlights the lines and the space between lines(due to line height being taller than 1) is not highlighted.
Is there any way to achieve this either in CSS or in JavaScript?
One way to do this is to give the marked text a top and bottom padding.
p {line-height:2}
mark {padding:.5em 0}
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
<mark>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</mark>
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
Hi you can use the span tag and set the background of that span so it will work like
<p>this is the dummy text without any background <span style='background:yellow'> This text with the background colour</span> this text have no background</p>

javascript to get paragraph of selected text in web page

After highlighting text, I would like to obtain the paragraph in which the selected text resides.
var select = window._content.document.getSelection();
Any pointers please?
This is actually rather hard to do because you have to consider six cases:
The selection is not within a paragraph (easy);
The entire selection is within one paragraph (easy);
The entire selection crosses one or more sibling paragraphs (harder);
The selection starts or ends in an element not within a paragraph (harder);
The paragraphs spanned are at different levels eg one is within a list item while two others are siblings of the list (even harder); and
Some combination of the above.
So firstly you have to decide how complete you want the solution to be. I'll only cover the simplest cases of (1) and (2).
function getSelectedParagraphText() {
if (window.getSelection) {
selection = window.getSelection();
} else if (document.selection) {
selection = document.selection.createRange();
}
var parent = selection.anchorNode;
while (parent != null && parent.localName != "P") {
parent = parent.parentNode;
}
if (parent == null) {
return "";
} else {
return parent.innerText || parent.textContent;
}
}
Note: If you're after tags too replace textContent with innerHTML.
Edit: Better version put in, including better browser compatibility.
I found this useful example.
It seems that some browsers support window.getSelection() while others support document.getSelection(). The example handle all these cases.
select.anchorNode.parentNode will return the parent node, in your case the tag and you can then get the text of that node.
var x = window.getSelection()
var z = x.anchorNode.parentNode
alert(z.innerHTML)
Make sure you look at window.getSelection() as well since document.getSelection is depreciated in firefox.
$.event.props.push('onTextSelect');
$(document).click(function(){
var str=window.getSelection().anchorNode.data;
var str=str.substring(window.getSelection().anchorOffset,window.getSelection().focusOffset);
if(str)
$(window.getSelection().focusNode.parentNode).trigger({type:'onTextSelect',text:str});
});
$('p').on('onTextSelect',function(e){
console.log($(this).attr('class'))
$('p:last').text(e.text);
});
html
<div><p class="p">some text</p></div>
<p></p>
You can find the fiddle here https://jsfiddle.net/q9nkc0fd/6/
A new project is born from jsmatita:
http://takenotes.sourceforge.net/
(it's in italian language)
I reproduced code from answer above from #Thiago Souza and created a snippet for that purpose for those who can provide a correct answer.
function getSelectedParagraph(){
const selection = window.getSelection();
let parent = selection.anchorNode;
while (parent != null && parent.nodeName != "P") {
parent = parent.parentNode;
};
console.log(parent);
return parent;
};
window.onload = getSelectedParagraph();
<div class='paragraph-container'>
<p id='paragraph-01'>
Paragraph 1: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
<p id='paragraph-02'>
Paragraph 2: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
<p id='paragraph-03'>
Paragraph 3: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
</div>
function getSelectedParagraph(){
const selection = window.getSelection();
let parent = selection.anchorNode;
while (parent != null && parent.nodeName != "P") {
parent = parent.parentNode;
}
return parent;
}

Categories

Resources