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(' - ##');
});
Related
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>
----------------------------------------------------
| 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/
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/
I'm creating a script that takes two input dimensions, width, and height, and creates a scaled grid which is representative of how many blocks could fit in a box with the given dimensions with the following function:
function makeRow() {
for (var i = 1; i <= blocksTall; i++) {
var mb = document.createElement("div");
mb.setAttribute("class", "matrix-block mb-off");
mb.setAttribute("onClick", "select_mb('" + j + "," + i + "');");
placeBlocks.appendChild(mb);
if (i = blocksWide) {
placeBlocks.appendChild('br');
}
}
}
This function works fine to display the first row of blocks, and then inserts a break tag after the row has finished being rendered, which is exactly what I want to do. The problem is I need to generate 17 more rows, with the same number of blocks, each one under the previous row, so my first thought was, I'll just wrap another for loop around this first for loop and since there is a break there, it will render the new row below the previous one:
for (var j = 1; j <= blocksTall; j++) { // Vertical for loop.
for (var i = 1; i <= blocksWide; i++) { // Horizontal for loop.
var mb = document.createElement("div");
//mb.setAttribute("id", "matblock-" + i + "-" + j);
mb.setAttribute("class", "matrix-block mb-off");
mb.setAttribute("onClick", "select_mb('" + i + "," + j + "');");
placeBlocks.appendChild(mb);
}
if (j = blocksWide) {
placeBlocks.appendChild(brk);
}
}
Where blocksWide = 17. Here is a fiddle with the complete script. When I log the value for j in the console, it does in fact increment (which tells me that the for loop is working). What seems to be happening though is that it is for some reason rendering the row, and then either rendering the new row on top of it (seems unlikely since the break tag is rendered after each row completes) or, for some reason the children are destroyed each time a new "horizontal" for loop is run.
Does anybody know why this might be happening and how to properly get each row to be appended under the last row so it produces a grid of blocks instead of just one row?
Thanks in advance, any help is greatly appreciated.
So, I'm a bit confused about some aspects of your script, but I think you have two major issues.
Firstly, you only ever call document.createElement("br") once, which means you only ever create a single line-break; and a single line-break can only appear in one place in the DOM. This:
placeBlocks.appendChild(brk);
removes brk from its current position in the DOM and then puts it at the end of placeBlocks. You should change it to this:
placeBlocks.appendChild(document.createElement("br"));
Secondly, I don't think that if (j = blocksWide) { makes sense. Note that it's equivalent to this:
j = blocksWide;
if (blocksWide != 0) {
which means that it interferes with your for-loop by manipulating the value of j. I think the fix for that issue is simply to remove the whole if-check, and to perform its body unconditionally.
I really don't understand what you were trying to do with the remainder operators and the dividing, but blocksWide resolved to infinity causing an infinite loop, and blocksHigh was just 17. All of the other variables besides full weren't used.
You don't actually need two loops, although it is ok to do that. If you want to use just one loop you basically just need to know if i is a multiple of dispW.
So you divide i by dispW then you want to know if it is an integer, to find this you use the remainder operator for 1 and if it resolves to 0 it is an interger. It looks like this...
if ((i / dispW) % 1 === 0)
// if ( dispW=3 && ( i=3 || i=6 || i=9 || ... ) ) true;
This in a loop would look like
totalWidth = dispW * dispH; // total number of blocks
for (var i = 1; i <= totalWidth; i++) {
// do stuff;
if((i / dispW) % 1 === 0) {
// insert new line break;
}
}
The method you used for selecting the blocks was a round about way of doing it. First you shouldn't use inline javascript, second you shouldn't use javascript to embed inline javascript in a dynamically created element. Use element.onclick = function; instead.
Notice there is no braces after the function. This is because you are actually passing the function reference and not the returned value of the function.
element.onclick passes an event object to the function reference. You can use this to select the block that was clicked on like so.
for ( ... ) {
...
var element = document.createElement('div');
element.onclick = myFunction;
...
}
function myFunction(e) {
var clicked = e.target // this is the element that was clicked on
}
Also, you were creating one <br> element outside of the loop. Because appendChild moves elements and does not create elements it will just keep moving the line break until the loop finishes. It should look like this.
placeBox.appendChild(document.createElement('br'))
// append a newly created line break;
Then even if all the logic worked as intended and you create a new line break every time, floated blocks means no line breaks use display: inline-block; instead.
So in the end what you get is...
(Full difference)
window.onload = function () {
renderGrid();
};
function renderGrid() {
var blocksTall = document.getElementById('height-in').value;
var blocksWide = document.getElementById('width-in').value;
var blocksTotal = blocksWide * blocksTall;
var placeBlocks = document.getElementById('matrix-shell');
while (placeBlocks.firstChild) {
placeBlocks.firstChild.remove();
}
console.log(blocksWide + "/" + blocksTall);
for (var i = 1; i <= blocksTotal; i++) {
var mb = document.createElement("div");
mb.className = 'matrix-block mb-off';
mb.onclick = select_mb;
placeBlocks.appendChild(mb);
if (((i / blocksWide) % 1) === 0) {
var brk = document.createElement("br");
placeBlocks.appendChild(brk);
}
}
}
function select_mb(e) {
var cur_mb = e.target;
if (cur_mb.className == "matrix-block mb-off") {
// Turn cell on.
cur_mb.style.backgroundColor = "#00FF00";
cur_mb.className = "matrix-block mb-on";
} else {
//Turn cell off.
cur_mb.style.backgroundColor = "#000";
cur_mb.className = "matrix-block mb-off";
}
}
.matrix-block {
height: 10px;
width: 10px;
border: 1px solid #fff;
display: inline-block;
background-color: black;
}
.mb-off {
background-color: black;
}
#matrix-shell {
font-size: 0;
display: inline-block;
border: 1px solid red;
white-space: nowrap;}
<table>
<tr>
<td>Width:</td>
<td>
<input id="width-in" name="width-in" type="text" />
</td>
</tr>
<tr>
<td>Height:</td>
<td>
<input id="height-in" name="height-in" type="text" />
</td>
</tr>
<tr>
<td colspan="2">
<button onClick="renderGrid()">Compute</button>
</td>
</tr>
</table>
<br/>
<div id="matrix-shell"></div>
I'm new to Javascript and I'm building a connect four game to learn about javascript. The problem is that I am unable to add margins between the 'o' so they all look clumped together.
I would like help adding margins or padding between the 'o's. Some thoughts are that I may need to add each row of 'o' in a table. However, I'm updating it through the javascript function. How do I get around that?
My javascript file
var first_row = ['o','o','o','o','o','o','o'];
onload = function ()
{
document.getElementById("row1").innerHTML = first_row;
}
HTML file
<h1 class="space" id="row1"></h1>
In pure javascript (not using jquery) with a for in loop than append spans with the '0's for styling.
var first_row = ['o','o','o','o','o','o','o'];
var row = document.getElementById('row1');
for (var i in first_row) {
var span = document.createElement('span');
span.innerHTML = first_row[i];
row.appendChild(span);
}
See here: http://jsfiddle.net/95JqK/17/
You'll have to encapsulate them into tags and put css rules on them.
A simple solution would be:
var first_row = ['<span>o</span>','<span>o</span>','<span>o</span>','<span>o</span>','<span>o</span>','<span>o</span>','<span>o</span>'];
onload = function ()
{
document.getElementById("row1").innerHTML = first_row;
}
And in a css file:
span {
margin: 0 10px 0 10px;
}
Perhaps you could use CSS on the class="space" elements
.space{
padding: 8px;
}