Text changes height after adding unicode character - html

I have HTML element with text content.
Font is set to sans-serif in CSS.
Text is updated via JavaScript.
Sometimes contains just ASCII characters but, sometimes, includes "➜" character. See following snippet:
var text = document.getElementById("text");
var chars = "A➜";
var i = 0;
function update() {
i=1-i;
text.innerText = "char: " + chars[i];
setTimeout(update, 500);
}
update();
div {
font-family: sans-serif;
background-color: lightgrey;
}
<div id="text" />
This works fine in IE11 but in Chrome the element "wiggles":
It looks like this happens because different font is used to render "➜" character:
Arial—Local file(5 glyphs)
Segoe UI Symbol—Local file(1 glyph)
Is there a simple way to stabilize the height of whole element and position of static part of text?
One way seems to be using "Segoe UI Symbol" for whole element - but I prefer a different font for regular text.

Just add a line-height style to your element:
var text = document.getElementById("text");
var chars = "A➜";
var i = 0;
function update() {
i=1-i;
text.innerText = "char: " + chars[i];
setTimeout(update, 500);
}
update();
div {
font-family: sans-serif;
background-color: lightgrey;
line-height: 1em;
}
<div id="text" />

An easy fix would be to set the line-height in CSS
var text = document.getElementById("x");
var chars = "A➜";
var i = 0;
function update() {
i=1-i;
text.innerText = chars[i];
setTimeout(update, 500);
}
update();
div {
font-family: sans-serif;
background-color: lightgrey;
line-height: 1em;
}
#x {
line-height: 1em;
}
<div id="text">char: <span id="x" /></div>

Related

How to imitate a monospace font with a variable-width font?

I have read CSS - Make sans-serif font imitate monospace font but the CSS rule letter-spacing doesn't seem to be enough:
How to imitate a monospace fixed font from a standard sans-serif font?
This doesn't work perfectly:
.text {
font-family: sans-serif;
letter-spacing: 10px;
}
<div class="text">
ABCDEFGHIJKLMNOPQR<br>
STUVWXYZ0123456789
</div>
letter-spacing just evenly inserts whitespace between all letters (...hence the name).
It won't normalize characters/glyphs to have the same widths.
We would need a css property like letter-width which doesn't exist.
Apart from changing the actual font metrics in a font editor and compiling a new font you could split up all letters into an array of <span> elements via javaScript.
emulateMonospace();
function emulateMonospace() {
let monoWraps = document.querySelectorAll(".toMonospace");
monoWraps.forEach(function(monoWrap, i) {
//remove all "\n" linebreaks and replace br tags with "\n"
monoWrap.innerHTML = monoWrap.innerHTML
.replaceAll("\n", "")
.replaceAll("<br>", "\n");
let text = monoWrap.textContent;
let letters = text.split("");
//get font-size
let style = window.getComputedStyle(monoWrap);
let fontSize = parseFloat(style.fontSize);
//find maximum letter width
let widths = [];
monoWrap.textContent = "";
letters.forEach(function(letter) {
let span = document.createElement("span");
if (letter == "\n") {
span = document.createElement("br");
}
if (letter == ' ') {
span.innerHTML = ' ';
} else {
span.textContent = letter;
}
monoWrap.appendChild(span);
let width = parseFloat(span.getBoundingClientRect().width);
widths.push(width);
span.classList.add("spanMono");
span.classList.add("spanMono" + i);
});
monoWrap.classList.replace("variableWidth", "monoSpace");
//get exact max width
let maxWidth = Math.max(...widths);
let maxEm = maxWidth / fontSize;
let newStyle = document.createElement("style");
document.head.appendChild(newStyle);
newStyle.sheet.insertRule(`.spanMono${i} { width: ${maxEm}em }`, 0);
});
}
body{
font-family: sans-serif;
font-size: 10vw;
line-height: 1.2em;
transition: 0.3s;
}
.monospaced{
font-family: monospace;
}
.letterspacing{
letter-spacing:0.3em;
}
.teko {
font-family: "Teko", sans-serif;
}
.serif{
font-family: "Georgia", serif;
}
.variableWidth {
opacity: 0;
}
.monoSpace {
opacity: 1;
}
.spanMono {
display: inline-block;
outline: 1px dotted #ccc;
text-align: center;
line-height:1em;
}
<link href="https://fonts.googleapis.com/css2?family=Teko:wght#300&display=swap" rel="stylesheet">
<h3 style="font-size:0.5em;">Proper monospace font</h3>
<div class="monospaced">
WiWi</br>
iWiW
</div>
<h3 style="font-size:0.5em;">Letterspacing can't emulate monospaced fonts!</h3>
<div class="letterspacing">
WiWi</br>
iWiW
</div>
<hr>
<h3 style="font-size:0.5em;">Text splitted up in spans</h3>
<div class="toMonospace variableWidth">
ABCDEFGHIJKLMNOPQR<br>
STUVWXYZ0123456789<br>
</div>
<div class="toMonospace variableWidth teko">
ABCDEFGHIJKLMNOPQR<br>
STUVWXYZ0123456789<br>
</div>
<div class="toMonospace variableWidth serif">
This is<br>
not a<br>
Monospace<br>
font!!!
</div>
Each character will be wrapped in a span with an inline-block display property.
Besides all characters are centered via text-align:center.
The above script will also compare the widths of all characters to set the largest width as the span width.
Admittedly, this is not very handy
but this approach might suffice for design/layout purposes and won't change the actual font files.
As illustrated in the snippet:
In monospace fonts the widest letters like "W" get squeezed (not distorted)
whereas the thinner ones like
"i" get visually stretched (e.g by adding bottom serifs).
So proper monospace fonts are completely different and can't really be emulated.
I've spent too much time trying to find a good monospaced font that works with several alphabets and had the look I wanted. These are the solutions I found (in order of recommendation):
Find a monospaced font that you like and use that.
Use a font editor and change all the letters to the same width (there might be a Python program that can do that, but I haven't tried it). But changing a font to monospaced will not look as good, there are a lots of craftsmanship in creating each letter so it will fit properly in the monospaced box.
Use letter-spacing to simulate a simple monospaced font.

Why does inline-block remove spaces between words?

Result has no space.
Code has a space between words.
How do I fix this? I need to use inline-block (or any inline display) because my animation won't work without it.
CSS
This is the javascript for the animation.
const text = document.querySelector("h1");
const strText = text.textContent;
const splitTxt = strText.split("");
text.textContent = "";
for(let i=0; i < splitTxt.length; i++){
text.innerHTML += "<span>"+ splitTxt[i] + "</span>";
}
let char = 0;
let timer = setInterval(onTick, 50);
function onTick() {
const span = text.querySelectorAll('span')[char];
span.classList.add('fade');
char++;
if(char === splitTxt.length) {
complete();
return;
}
}
function complete() {
clearInterval(timer);
timer = null;
}
Edit: I fixed this by putting a in between the letters.
If you want every word of the h1 to be inside of a span tag, you should do the split with a whitespace, like this:
const splitTxt = strText.split(" ");
In your code you have the quotes inside the split function without the space between, and that will cause that every character to be in a span.
You mean this?
header {
position:relative;
height: 200px;
width: 200px;
border: 1px solid #333;
display: flex;
align-items: center;
justify-content: center;
}
<header>
<h1>
myText
</h1>
</header>

HTML - How to display "LOGIN" if member name CLASS is empty

Basically it would would be super great if someone can provide a pointer on how to achieve this.
I use a 3rd SAS and when logged in to it, it returns the username to class="SFnam" which can be displayed on any page that has the associated JS embedded in the bottom of the page.
At the moment I use the following html to display the Users Name in the top right of the screen when logged in: i.e. "Logged in as: BORIS SMITH", however when a users NOT not logged in it displays "Logged in as:". Not ideal.
How can I display "LOGIN" if the class="SFnam" is empty?
Here's what I'm using (After Rafaels input):
<script type="text/javascript">
window.addEventListener("DOMContentLoaded", function(event) {
var spanElm = document.getElementById('myspan');
if(spanElm.classList.contains('SFnam'))
{
console.log('here');
}
else
{
console.log('here instead');
document.getElementsByTagName('user')[0].innerHTML = "LOGIN";
}
});
</script>
<style>
#loginwrapper {
margin: 0px 0px 0px Opx;
border: none;
cursor: pointer;
}
.logincontainer { text-align: right;}
user {
display: inline-flex;
background-color: #1f7665; color: #fff;
padding:2px 3px 1px 3px;
font-size:8px;
margin-right: 10px;
border-radius: 3px;
font-family: Raleway, Arial, Helvetica, 'Liberation Sans', FreeSans, sans-serif;
}
</style>
<div id="loginwrapper" title ="Click to Log in/out" onclick="window.open('/member-login.html','_self')">
<div class="logincontainer">
<user>Logged in as: <b><span style="text-transform:uppercase;" class="SFnam" id='myspan'></span></b>
</div>
</div>
When logged in the span result is:
< span class="SFnam">Boris Smith< /span >
When logged out the span result is:
< span class="SFnam">< /span>
I am reasonably new to this however have some skills with basic coding etc so apologies if the above has not been constructed poorly :(.
Any guidance / assistance will be gratefully appreciated.
Cheers
Boris
EDITED:
Also of note is the Listener script at the bottom of each page (not sure if that helps?:
<script>(function(){var i,j,a,x;try{x=localStorage.getItem("SF_nam");}catch(e){x="";}
try{for(a=document.querySelectorAll(".SFnam"),i=a.length-1;i>=0;i--)a[i].innerHTML=x?x:"";}catch(e){}
try{for(a=document.querySelectorAll(".SF_li"),i=a.length-1;i>=0;i--)a[i].style.display=x?"":"none";}catch(e){}
try{for(a=document.querySelectorAll(".SF_lo"),i=a.length-1;i>=0;i--)a[i].style.display=x?"none":"";}catch(e){}})();
</script>
This will change the element's text accordingly to that span class containing text or not.
function updateSpan()
{
var spanElm = document.getElementById('myspan');
if(spanElm.innerHTML)
{
document.getElementsByTagName('user')[0].innerHTML = "Logged in as " + spanElm.innerHTML;
}
else
{
document.getElementsByTagName('user')[0].innerHTML = "LOGIN";
}
}
window.addEventListener("DOMContentLoaded", function(event) {
updateSpan();
});
add an id to your span so we can locate it
<span style="text-transform:uppercase;"class="SFnam" id='myspan'></span>
update your bottom script with this
<script>(function(){var i,j,a,x;try{x=localStorage.getItem("SF_nam");}catch(e){x="";}
try{for(a=document.querySelectorAll(".SFnam"),i=a.length-1;i>=0;i--)a[i].innerHTML=x?x:"";}catch(e){}
try{for(a=document.querySelectorAll(".SF_li"),i=a.length-1;i>=0;i--)a[i].style.display=x?"":"none";}catch(e){}
try{for(a=document.querySelectorAll(".SF_lo"),i=a.length-1;i>=0;i--)a[i].style.display=x?"none":"";}catch(e){}
updateSpan()})();
</script>
This will make sure the function is also updated if changes happen during execution time.
Here's a JSFiddle

Why is my span moving down when I add additional text?

I am working on a project that involves a span of text. The span is added to by javascript, for example if I add 'a' to the span, the formatting is fine, but if I add a second character, 'b', both characters and all characters written after that move down about a few pixels, and stay at that line. The span must always display text in the same place, I'm using a monotype font so there's uniform display. I thought maybe the span was wordwrapping or something. Strangely, I've used this same span and code on my Wordpress site with no issues. It's just the standalone html, javascript, and CSS that are having the issue. My current CSS for the span looks like this. I also tried copying the CSS profile from Wordpress but didn't see anything obvious that would be changing where the text position after adding a second character.
#dlpasscode {
display:block;
z-index: 2;
width: 170px;
top: 144px;
left: 120px;
font-family: 'Ubuntu Mono';
font-size: 28px;
color: white;
margin-top:0;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
margin: 0;
padding: 0;
border: 0;
font-size: 28px;
vertical-align: baseline;
white-space:nowrap;
overflow: hidden;}
The javascript I'm calling to display the characters (this is for a UI simulator and each ID is passed in as a character then added to the string).
function dlDisplayPass(lastOptScreen) {
switch (lastOptScreen) {
case dlSim.enterNewPass.src:
case dlSim.confNewPass.src:
passStore = passStore + clicked.id;
jQuery('#dlpasscode').append(clicked.id);
break;
default:
// display up to 9 masked characters, stop and need to display arrow
// allows only up to 32 characters, ignores clicks after that and beeps
if (passStore.length < 32) {
passStore = passStore + clicked.id;
} else {
longbeep.playclip();
}
var passArray = null;
if (passStore.length <= 8) {
passArray = new Array(passStore.length).join(dlpassmask);
}
if (passStore.length === 9) {
passArray = new Array(9).join(dlpassmask);
}
if (passStore.length >= 10 && passStore.length < 32) {
passArray = leftArrow + new Array(8).join(dlpassmask);
}
if (passStore.length < 32) {
jQuery('#dlpasscode').text(passArray).append(clicked.id);
}
}
return false;
}
The actual span just looks like this
<span class="dlbezel dlchild" id="dlpasscode" unselectable ></span>
I figured out updating jQuery updated the issue. Per their website, the jQuery().text() function says "due to variations in the HTML parsers in different browsers, the text returned may vary in newlines and other white space."
Thanks for the help.

Autoresize text to fill fixed size container using HTML only

Needing a solution to autoresize text in a fixed sized container. A single word should appear really large filling the container. The longer the string the smaller the font becomes as it resizes to fit on one line.
The code I've found (see below) is almost there but for some reason wraps the text onto a second line. Any suggestions on how I could fix it so no matter how long the string of text it will resize and always be just a single line of text?
Thanks
<html>
<head>
<style type="text/css">
#dynamicDiv
{
background: #CCCCCC;
width: 240px;
height: 64px;
font-size: 64px;
overflow: hidden;
}
</style>
<script type="text/javascript">
function shrink()
{
var textSpan = document.getElementById("dynamicSpan");
var textDiv = document.getElementById("dynamicDiv");
textSpan.style.fontSize = 64;
while(textSpan.offsetHeight > textDiv.offsetHeight)
{
textSpan.style.fontSize = parseInt(textSpan.style.fontSize) - 1;
}
}
</script>
</head>
<body onload="shrink()">
<div align='center' id="dynamicDiv"><span id="dynamicSpan">Here is a string of text I want on just one line</span></div>
</body>
</html>
This code works, but I am using a little jQuery, I hope this helps in some way...
<style type="text/css">
#container {
height:100px;
background-color:#eeeeee;
text-align:center;
font-family:Myriad Pro;
}
<style>
<div id="container">01234567890123456789</div>
<script type="text/javascript">
$(document).ready(function()
{
var w = parseInt($("#container").width());
var l = parseInt($("#container").html().length);
var fontSize = ( (w / l) * 2 ) - 2;
fontSize = fontSize+'px';
$("#container").css('font-size', fontSize);
});
</script>