Get each line of text element - javascript

Is there a way in Vanilla JS to get each line of a text element when the page loads or when the window is resized?
Lets say for the element <h2>This is a beautiful example text</h2>.
On mobile (with less space it breaks into several lines) would be displayed as:
This is a
beautiful
example text
and i would like to get an array like:
["This is a", "beautiful", "example text"]
if we resize the window, maybe on desktop, than the text would have a little bit more space so it needs to break in to less lines, maybe:
This is a beautiful
example text
and then i would like to get:
["This is a beautiful", "example text"]
i have tried something like:
let text = document.querySelector('.text').textContent;
let lines = text.split(/\r|\r\n|\n/);
but i always end up getting the whole string together like ["This is a beautiful text"].
I am trying to do this, because i need to style each line independently (yeap, crazy client wishes!), i cannot change the html and it needs to work when i resize the window. So i think i need to do it with JS, and somehow see how CSS is affecting the text and breaking it into several lines...
so, to clarify, what i am trying to get with JS with the text of each line, when the text breaks because of the container getting smaller. So the Element and the text are the same, but due to less space it breaks into different amount of lines.
Any ideas?
** === UPDATE === **
I can get the number of lines using .getClientRects(). But what i am trying to do is get the text of each of the lines of the element

I've put together a mock up JS solution.
Overview:
Get an array of words
Append each word one at a time to a hidden element with the same font-sizing styles as the title
Check if the element is larger than the title, if so, add the current line (before we added the last word to it) to an array
function calcLines() {
// Build an array of each word used in the original title
var allWords = document.getElementById("title").innerText.match(/\S+/g) || [];
// The array we will fill with each line
var lines = [];
// The current line we are working on building
var currentLine = "";
// Work through the words until we're filling the correct amount of space
for (var i = 0; i < allWords.length; i++) {
// Build a new line and check if it is now too large for the container
var newLine = currentLine + allWords[i] + " ";
document.getElementById("fontSizeTester").innerText = newLine;
if (
document.getElementById("fontSizeTester").clientWidth >
document.getElementById("title").clientWidth
) {
// If the line is now larger, use the previous line (without the last added word) and reset the current line to just the last word
lines.push(currentLine.trim());
currentLine = allWords[i] + " ";
} else {
// If it's not long enough yet, just keep adding words
currentLine = newLine;
}
}
// Push any unfinshed final line to the array
lines.push(currentLine.trim());
console.log(lines);
}
// Run on load and on resize
calcLines();
window.addEventListener("resize", calcLines);
h2 {
font-size: 30px;
font-weight: normal;
font-family: arial;
}
#fontSizeTester {
position: absolute;
visibility: hidden;
height: auto;
width: auto;
white-space: nowrap;
}
<h2 id="title">This is a beautiful example text This is a beautiful example text This is a beautiful example text This is a beautiful example text This is a beautiful example text This is a beautiful example text This is a beautiful example text This is a beautiful example text This is a beautiful example text</h2>
<h2 id="fontSizeTester"></h2>

You can create a flex container in your body , and attach your array there. As result all items in your array will be filling the width of the screen.
https://codepen.io/Vlasenko/pen/vYLwMvE - check it here
const cont = document.querySelector(".container");
const arr = ["This is a", "beautiful", "example text"];
arr.forEach((item) => {
const div = document.createElement("div");
div.className = "item";
div.innerText = item;
cont.appendChild(div);
});
<style>
.container {
display: flex;
flex-wrap: wrap;
}
.item {
padding-right: 5px;
}
</style>
<body>
<div class="container"></div>
</body>

Related

Is it possible to apply style changes to a specific value of an object?

I have a p that shows dozens of different texts on specific mouse events.
Is it possible to have just one of this texts displayed with a different style?
I made a function which changes p.style.attributes but this function have to fire everytime the text in p changes, so I'm asking if there is a smoother way to do this. Is possible to give style to a value?
Let's make clear example:
var text32 = "this text should be in blue and huge"
Is it possible to give style values to a var like that?
You can add class on text change on whichever text you want. I have created simple example below where I am changing the Text of p tag on click and showing the different style on 3rd one. Again, this is just a simple example and the trick is you can change the Class whenever you want.
See the Snippet below:
var pTag = document.getElementById("pTag");
var currentIndex = 0;
var pText = ["This is Text 1", "This is Text 2", "This is Text 3", "This is Text 4", "This is Text 5"];
var pBold = [false, false, true, false, false];
pTag.addEventListener("click", function(){
currentIndex++;
if(currentIndex >= pText.length)
currentIndex = 0;
pTag.innerHTML = pText[currentIndex];
if(pBold[currentIndex])
pTag.classList.add("redBold");
else
pTag.classList.remove("redBold");
})
p{
cursor: pointer;
user-select:none;
}
.redBold{
color:red;
font-weight: bold;
}
span{
font-style: italic;
}
<p id="pTag">Click Me!!!!!!</p>
<span>On click of Above p ("Click Me!!!!!"), it will change the text. When the text will be 'This is Text 3', it should be in Red and Bold.</span>
You can test it here also.
You said you have a p tag with multiple elements. So the js should be
var p = document.getElementsByTagName("p");
for(var i=0; i<p.length; i++) {
if(p[i].innerHTML == "text huge blue"){
p[i].style.color = "blue";
p[i].style.font = "italic bold 20px arial,serif";
}
else
p[i].style.color = "red";
}
<p>text huge blue</p>
<p>text huge other things</p>
In this snippet you have an array made by all the elements in the document with "p" tag, which i assume is your case (otherwise is quite easy to edit this code to apply it to your needs). If the content is "text huge blue" then make p's content blue huge and italic, otherwise make it red. So I provided you two examples to see the difference.

How to get number of visible characters inside a div(till its width), which has overflow text [duplicate]

----------------------------------------------------
| This is my text inside a div and I want the overf|low of the text to be cut
----------------------------------------------------
Please note that I want the overflow to be removed, so the CSS ellipsis property would not work for me. So basically, I want that the text above to appear like this:
----------------------------------------------------
| This is my text inside a div and I want the overf|
----------------------------------------------------
How is this possible with pure JavaScript?
EDIT
I need a JavaScript function to crop the text because I need to count the characters of the visible text.
Okay, I didn't see the addendum to the question. Although I had previously said it wasn't possible to do this using JavaScript and a font that isn't fixed-width... it actually is possible!
You can wrap each individual character in a <span>, and find the first <span> that is outside the bounds of the parent. Something like:
function countVisibleCharacters(element) {
var text = element.firstChild.nodeValue;
var r = 0;
element.removeChild(element.firstChild);
for(var i = 0; i < text.length; i++) {
var newNode = document.createElement('span');
newNode.appendChild(document.createTextNode(text.charAt(i)));
element.appendChild(newNode);
if(newNode.offsetLeft < element.offsetWidth) {
r++;
}
}
return r;
}
Here's a demo.
You can do this with Javascript. Here is a function that counts the number of visible characters in an element, regardless of external css sheets and inline styles applied to the element. I've only tested it in Chrome, but I think it is cross browser friendly:
function count_visible(el){
var padding, em, numc;
var text = el.firstChild.data;
var max = el.clientWidth;
var tmp = document.createElement('span');
var node = document.createTextNode();
tmp.appendChild(node);
document.body.appendChild(tmp);
if(getComputedStyle)
tmp.style.cssText = getComputedStyle(el, null).cssText;
else if(el.currentStyle)
tmp.style.cssText = el.currentStyle.cssText;
tmp.style.position = 'absolute';
tmp.style.overflow = 'visible';
tmp.style.width = 'auto';
// Estimate number of characters that can fit.
padding = tmp.style.padding;
tmp.style.padding = '0';
tmp.innerHTML = 'M';
el.parentNode.appendChild(tmp);
em = tmp.clientWidth;
tmp.style.padding = padding;
numc = Math.floor(max/em);
var width = tmp.clientWidth;
// Only one of the following loops will iterate more than one time
// Depending on if we overestimated or underestimated.
// Add characters until we reach overflow width
while(width < max && numc <= text.length){
node.nodeValue = text.substring(0, ++numc);
width = tmp.clientWidth;
}
// Remove characters until we no longer have overflow
while(width > max && numc){
node.nodeValue = text.substring(0, --numc);
width = tmp.clientWidth;
}
// Remove temporary div
document.body.removeChild(tmp);
return numc;
}
JSFiddle Example
You're trying to force a CSS problem into JavaScript. Put the hammer away and get out a screwdriver. http://en.wiktionary.org/wiki/if_all_you_have_is_a_hammer,_everything_looks_like_a_nail
Solving the answer of character count is probably irrelevant if you take a step back. The last character could be only partially visible, and character count is drastically different given font size changes, the difference of width between W an i, etc. Probably the div's width is more important than the character count in the true problem.
If you're still stuck on figuring out the characters visible, put a span inside the div around the text, use the css provided in other answers to this question, and then in JavaScript trim one character at a time off the string until the span's width is less than the div's width. And then watch as your browser freezes for a few seconds every time you do that to a big paragraph.
try this, it requires a fixed width if that is ok with you: http://jsfiddle.net/timrpeterson/qvZKw/20/
HTML:
<div class="col">This is my text inside a div and I want the overf|low of the text to be cut</div>
CSS:
.col {
width:120px;
overflow: hidden;
white-space:nowrap;
}​
.col { width:40px; overflow: hidden; white-space:nowrap; }
White-space: nowrap; is needed when the content has spaces.
Either way, long words in single lines do not appear. http://jsfiddle.net/h6Bhb/

Add Content Using Jquery

My demo in JS Fiddle https://jsfiddle.net/dineshkanivu/5fp2sjgb/2/
I want to add content Dynamically to the id="myNote" in its 4th line.
click the button lines , you can see total number of lines. i want to add some html content after 4th line. How can i do this using jQuery
Snippet :
$(function() {
$("#getLines").click(function() {
var myheight = $("#myNote").height();
parseFloat($("#myNote").css("line-height"));
//$('#myNote').after('<button>button</button>');
alert(myheight);
});
});
#myNote {
width: 300px;
line-height: 1;
height: auto;
text-align: justify;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="myNote">
Finally, designing the last sentence in this way has the added benefit of seamlessly moving the reader to the first paragraph of the body of the paper. In this way we can see that the basic introduction does not need to be much more than three or four
sentences in length. If yours is much longer you might want to consider editing it down a bit! Here, by way of example, is an introductory paragraph to an essay in response to the following question:
</div>
<button id="getLines">lines</button>
According to this post I wrote a little function to do this.
Surely there a more efficent way. But it works fine.
I wrap every word in an own span. After that I check the positions of all spans, get the line number and add a class with this line number to the span.
function insertTextAtLine(target,lineNumber,textInsert){
var words = target.text().split(' ');
var text = '';
$.each(words, function(i, w){
if($.trim(w)) text = text + '<span>' + w + '</span> ';
});
target.html(text);
var line = 0;
var prevTop = - parseFloat($("#myNote").css("line-height"));
$('span', target).each(function(){
var word = $(this);
var top = word.offset().top;
if(top != prevTop){
prevTop = top;
line++;
}
word.attr('class', 'line' + line);
});
var insert=$('<span />').text(textInsert);
target.find('.line'+lineNumber).first().prepend(insert);
};
Fiddle:https://jsfiddle.net/tye3czva/4/

Change Row Element with Javascript While Keeping Everything in 1 Line

I am currently using Javascript to add some text to multiple <td> elements of a webpage but the problem is that when I add the text, it spans 2 lines instead of 1.
How can I make it so it stays on one line, I don't mind if the element becomes wider.
Here is the page I am modifying and here is my code:
var Opp = document.querySelectorAll('td.P-xs')
for(i=0;i<Opp.length;i++){
Opp[i].innerHTML = Opp[i].innerHTML + " - ##"
}
And here is what it looks like.
Is there a way to make it be on one line?
In CSS, you can do something along the lines of
td {
white-space: nowrap;
}
http://www.w3schools.com/cssref/pr_text_white-space.asp
EDIT
I noticed that in the example, there is a <div> inside the td, which is a block element, forcing the next text (which you are adding) to the next line. If you do white-space: nowrap it only affects the inline (or inline block) elements. So you either need to make the div inline block, i.e.
td {
white-space: nowrap;
}
td div {
display: inline-block; /* inline would also work in this case */
}
or via JS:
var Opp = document.querySelectorAll('td.P-xs');
for(var i = 0; i < Opp.length; i++){
Opp[i].innerHTML = Opp[i].innerHTML + " - ##";
Opp[i].style.whiteSpace = "no-wrap";
Opp[i].firstElementChild.style.display = "inline-block";
}
Or you need to add your text within the div, i.e.
var Opp = document.querySelectorAll('td.P-xs');
for(var i = 0; i < Opp.length; i++){
Opp[i].style.whiteSpace = "no-wrap";
Opp[i].firstElementChild.innerHTML += " - ##";
}
For starters you adding it outside of a block element so you would need to do:
var Opp = document.querySelectorAll('td.P-xs')
for(i=0;i<Opp.length;i++){
Opp[i].firstElementChild.innerHTML = Opp[i].firstElementChild.innerHTML + " - ##"
}
then you can use white-space: nowrap; on the elements you don't want to wrap
I don't think it's a script issue but instead a spacing issue, probably the <td> cells are too small and automatically display their content on two lines. In fact as shown in this JSFiddle which I wrote with jQuery instead the content goes on a single line.
$.each($('td.P-xs'), function(){
$(this).append(' - ##');
});

How to create clozed text in HTML/CSS?

How can I cloze some text in an HTML document? Some marked text is hidden, replaced with an underline of exactly the same length as the original text and all of the words in the sentence should appear in precisely the same places they would appear had there not been a cloze. E.g.:
Once upon a time there ______ a cat.
The word to be clozed might be marked like this:
Once upon a time there <div class="cloze">lived</div> a cat.
Is there some way to hide the text and create an underline of exactly the same length?
Use some CSS:
.clozed {
border-bottom:1px solid black;
color:transparent;
}
This is <span class=clozed>text</span>.
Fiddle
Adding to #HoboSapiens answer (sorry, I can't comment due to score), you may also want to use the ::selection selector to ensure users can't drag across the area to uncover the text.
http://jsfiddle.net/Delorian/mm8tp1xb/
.clozed::selection {
color: transparent;
}
Note that users will always be able to see the text in the source, so if you want something more effective, you will need to use JavaScript on the client or server-side script to replace the text. However you will lose the benefit of having the underline at an accurate width.
You should do it via PHP, because if user would look into source code, he'll see the original text.
But if you need it in JS, you can implement it like this:
var spans = document.querySelectorAll('.clozed');
var words = [];
function repeat (str, count) {
var text = '';
var i;
for (i = 0; i < count; i++) {
text += str;
}
return text;
}
var i, c = spans.length;
for (i = 0; i < c; i++) {
var span = spans[i];
var text = span.innerText = span.textContent;
words.push(text);
span.innerText = span.textContent = repeat('_', text.length);
}
http://jsfiddle.net/0e7hw2a9/
Note: you may use words array to validate the input later.
Update: undo function: http://jsfiddle.net/0e7hw2a9/2/

Categories