What unicode spaces are equal to letter widths? - html

I am trying to replace some letters of a text (in a non monospace font) with spaces of same width.
Is it possible? How?
Are there any non-monospace fonts that allow this?
I found these unicode spaces, and apparently U+2002 is an EN SPACE and U+2003 an EM SPACE.
But the widths don't always match.
<p>the en space looks nice</p>
<p>the e  space looks  ice</p>
<p>but em space has more width</p>
<p>but e  space has  ore width</p>
And I wonder what spaces should I use for other letters...
I know I could set transparency or color to individual letters. But I wanted to find an easy solution with spaces.

Your question was interesting so I thought about the fact that you wanted a real space instead of the missing char, typically for a copy-paste operation. Using CSS pseudo-elements could be a solution because the content of them isn't used for text selection.
The space chars have a fixed width, which is not the case for letters since a m is wider than a i. I think that EM space is 1 em wide and EN space is 0.5 em.
So my idea was to replace your char, let's say m, by this:
<span class="pseudo-space" data-char="m"><span> </span></span>
Yes, this is a lot of HTML and you are asking yourself "why having an inner span?". Well, the problem is that we want the space to have no width and we want the pseudo-element to take this space instead. I also noticed that if you replace the non-breaking space by the normal space then you get no space at all when you select the text for a copy-paste.
I came out to this solution:
span.pseudo-space {
position: relative; /* Child span will be absolute. */
overflow: hidden; /* Just to avoid a potential scrollbar. */
}
span.pseudo-space > span {
position: absolute; /* Avoid width calculation on the parent span. */
}
span.pseudo-space::after {
content: attr(data-char); /* Display the original char. */
color: transparent;
}
/* Just to have a gray block to see the text aligned. */
blockquote {
margin: 0.5em 0;
padding: 1em;
background: #eee;
display: inline-block;
}
blockquote p:first-child {
margin-top: 0;
}
blockquote p:last-child {
margin-bottom: 0;
}
<blockquote>
<p>The "n" character will never be the same width as a space character.</p>
<p>The "<span class="pseudo-space" data-char="n"><span> </span></span>" character will <span class="pseudo-space" data-char="n"><span> </span></span>ever be the same width as a space character.</p>
<p>The "m" character will never be the same width as a space character.</p>
<p>The "<span class="pseudo-space" data-char="m"><span> </span></span>" character will never be the sa<span class="pseudo-space" data-char="m"><span> </span></span>e width as a space character.</p>
</blockquote>
Try to copy-paste the content of the blockquote and you should normally get the space instead of the hidden char. (Tested on Chrome, Firefox and Edge).

I wanted to do something like this.
If I could replace letters with spaces of the same width, it would be much easier. I wouldn't need to play with HTML elements and style.
// Convert letter to spans with opacity:0
const p = document.querySelector("p");
p.innerHTML = p.innerText
.split("")
.map(c => `<span class='off'>${c}</span>`)
.join("");
const letterSpans = p.querySelectorAll('span');
// Create random sequence of indexes
const indexes = Array(letterSpans.length).fill().map((e, i) => i)
indexes.sort((a, b) => 0.5 - Math.random());
// Display letters in the order indicated by `indexes`
function displayLetterAtIndex(i) {
letterSpans[indexes[i]].classList = "on";
if (i < letterSpans.length - 1) {
setTimeout(() => displayLetterAtIndex(i+1), 50);
}
}
displayLetterAtIndex(0);
.on {
opacity: 1;
}
.off {
opacity: 0;
}
span {
transition: opacity 1s linear;
}
<p>Letters will appear in a random order</p>

Related

Can I allow inline-block elements to split across lines?

I'd like to have links zoom in when the mouse hovers on them, I've tried with transform unsuccessfully, but then I found this answer, which looked promising.
However, making an inline element an inline-block also seems to prevent it from being split across several lines, as shown in the snippet below, which can create very unpleasant results for short width of the enclosing box.
div {
border: 1px solid black;
text-align: justify;
width: 20em;
}
a {
text-decoration: none;
display: inline-block;
}
a:hover {
transform: scale(1.01);
}
<div>
<p>Today, <a href="https://github.com/Aster89/WinZoZ">my
Vim plugin for easy window navigation</a>, which I named
WinZoZ,
has got its first star! Given <a
href="https://stackoverflow.com/questions/69007954/vim-remap-ctrl-w-in-insert-mode-to-behave-as-it-does-in-normal-mode#comment121984179_69007954">this
comment on StackOverflow</a>, the star is from the user #yolenoyer.
</p>
</div>
On the other hand, in this specific example above I see that the first link is so long that it does split across lines, so it looks like inline-block elements can indeed do that. How can allow it when they are shorter than the text width?
The animation missing is due to the original link (a tag) element not having the transition: property defined. Per the positioning documentation here it seems only inline-block is suitable for flowing text and that fails to show wrapped text, even with wrap: break-word; present. The inline-flex, inline-grid don't work either since they are both block display types.
One solution would be to break the text lines at certain points and setting different <br> elements to show at different #media widths for certain page widths/devices. However the inline-block elements cannot wrap like normal text, so the breaks just end up making it a 2-line block in the middle of the text.
div {
border: 1px solid black;
/* text-align: justify; */
width: 20em;
}
a {
text-decoration: none;
/* new */
transition: transform .15s; /* Animations: "transform" on a-tag must be present */
display: inline-block;
}
a:hover {
transform: scale(1.01); /* then we transform the transition here */
-ms-transform: scale(1.01); /* IE 9 */
-webkit-transform: scale(1.01); /* Safari 3-8 */
}
<div>
<p>Today, <a href="https://github.com/Aster89/WinZoZ">my
Vim plugin for easy window navigation</a>, which I named
WinZoZ,
has got its first star! Given <a
href="https://stackoverflow.com/questions/69007954/vim-remap-ctrl-w-in-insert-mode-to-behave-as-it-does-in-normal-mode#comment121984179_69007954">this
comment on StackOverflow</a>, the star is from the user #yolenoyer.
</p>
</div>
Some JS scripting
A breaking up of the line into blocks in a ul list with the li items being inline-block themselves could be a solution. A function to run at DOM load on each desired div's contents could do this. A loop for all a elements in those divs that transform each of the links into an array of words and puts the array items in a ul -> li. Perhaps there is a jQuery plugin for this already.
Light JS example
(not complete code, but using querySelectorAll, which could be used to gather the links from a <div> with a class you put as the function input):
<script type="text/javascript">
// function to look for a-tags in a DIV with a specific class
function linkToList(inputDivClass) {
// get the links inside the div with the input div class
const allLinks = document.querySelectorAll("div." + inputDivClass + " > a");
for (var i = allLinks.length; i < 0; i++) {
// here we go through the links returned from the div...
}
// then go through the data and see what to put where...
}
// when dom is loaded we run the function that looks for the divs with the a-tags...
document.addEventListener("DOMContentLoaded", linkToList(inputDivClass) );
</script>

HTML (With a little php) - Can't maintain indent of text on wrapping text

I've been on this problem for hours. I'm using PHP to display some HTML. It works, but I can't maintain the text indent on a long wrapping line of inserted text.
I've recreated the issue in HTML with the same issue for your convenience.
<style>
.indent {
padding-left: 1.5em;
text-indent:-1.5em;
}
</style>
<html>
<main>
<b class="do_something">X</b> <span class="indent"> Here are some words. When it wraps to the next line I really want them to stay in line with everything in the span, under the word HERE, rather than return under the BOLD "X" value. Cheers for the help. </span>
</main>
</html>
Now I've come close to fixing it using a display block, but alas the block creates a new line in the span and I need to stay on the same line as the X, which is important. I also tried flex but no joy.
Any ideas? Thanks.
Here's my php, which probably isn't relevant.
echo '<b class="do_something">X</b>', str_repeat(' ', 3);
echo '<span class = "indent">', nl2br($insert_words), '</span><br>';
echo '<hr>';
//And my other CSS:
.indent {
display:block;
margin-left:25px;
}
The problem is that you are using span which is an inline element.
You also do not need text-indent. It is giving you unexpected results because it only applies to one line of text, it has no effect on the lines that wrap.
You could achieve the desired result using flex like on the following example. I added some background color so you can see how the elements align themselves.
.indent {
padding-left: 1.5em;
text-indent: -1.5em;
background-color: #f8d7da;
}
.do_something {
background-color: #fff3cd;
}
#flex-container {
display: flex;
}
#flex-second {
padding-left: 1em;
background-color: #d4edda;
}
#x { background-color: #cce5ff; }
#fixed-width-x {
float: left;
}
<p>Example using flex</p>
<div id="flex-container">
<div id="x"><strong>X</strong></div>
<div id="flex-second"> Here are some words. When it wraps to the next line I really want them to stay in line with everything in the span, under the word HERE, rather than return under the BOLD "X" value. Cheers for the help. </div>
</div>
<p>Your example below</p>
<main>
<b class="do_something">X</b> <span class="indent"> Here are some words. When it wraps to the next line I really want them to stay in line with everything in the span, under the word HERE, rather than return under the BOLD "X" value. Cheers for the help. </span>
</main>

How can I scale arbitrary text to always fit the viewport width?

A site I'm busy working on has a section with some very large headings. There's something I'm not sure how to handle:
The heading may be one two short or long words, e.g: "Cyprus" to "Nouvelle Zelande", and it must scale to be roughly the width of the viewport. That means "Cyprus", being shorter, will have larger individual characters than longer text than "Nouvelle Zelande".
This would be relatively easy to do with JavaScript, I think, but I'd like to go for a pure HTML/CSS solution. So: how can I scale text to fit the width of the viewport? So far, I'm stumped and not sure how to do it, myself.
Some details:
You only need to target the most recent version of each browser, which includes IE11.
You may use any and all HTML5 and CSS3 that works within those browsers.
It's okay if you make the text "Nouvelle Zelande" word-wrap, as long as the longer of the two words still roughly fits to the width available.
You may add extra elements inside/around the headings.
Note that viewport units are not a solution. Previous questions asking about this (Pure CSS to make font-size responsive based on dynamic amount of characters, Font scaling based on width of container) have answers of "use viewport units, like vw!", but that doesn't handle this scenario at all, and astute readers even pointed this out. I've even used vw in the code sample below to demonstrate its non-solution-ness. It'll size based on the viewport just fine, but won't do any sizing based on the amount of text.
Code sample
h2 {
font-family: sans-serif;
text-transform: uppercase;
font-size: 14vw;
white-space: nowrap;
overflow-x: hidden;
margin: 0;
}
<h2>Nouvelle Zelande</h2>
<h2>Australia</h2>
<h2>Cyprus</h2>
The only unit, if being used to set font size, that is relative to the size of its container, is viewport units vw/vh, which will not solve your case alone, even if the container is the same width as the viewport, since it does not calc the letter size to fit into the container.
The closest non-script solution I can come up with is to use the CSS element counter trick, and wrap each letter in a span
The 130vw I set here, worked best for the given font, though this might need to be adjusted based on which font family is being used.
h2 {
display: inline-block;
font-family: sans-serif;
text-transform: uppercase;
white-space: nowrap;
overflow-x: hidden;
margin: 0;
}
/* 1 letter */
h2 span:first-child:nth-last-child(1) {
font-size: 130vw;
}
/* skipped 2-5 in this demo */
/* 6 letters */
h2 span:first-child:nth-last-child(6),
h2 span:first-child:nth-last-child(6) ~ span {
font-size: calc(130vw / 6);
}
/* skipped 7-14 in this demo */
/* 15 letters */
h2 span:first-child:nth-last-child(15),
h2 span:first-child:nth-last-child(15) ~ span {
font-size: calc(130vw / 15);
}
<h2><span>N</span><span>o</span><span>u</span><span>v</span><span>e</span><span>l</span><span>l</span><span>e</span> <span>Z</span><span>e</span><span>l</span><span>a</span><span>n</span><span>d</span><span>e</span></h2><br>
<h2><span>C</span><span>y</span><span>p</span><span>r</span><span>u</span><span>s</span></h2>
Here is the same concept using a script, and without the span's
(function (d,t) {
window.addEventListener("resize", throttler, false);
window.addEventListener("load", throttler(), false); /* run once on load to init */
function throttler() {
if ( !t ) {
t = setTimeout(function() {
t = null;
keepTextFit(d.querySelectorAll('h2'));
}, 66);
}
}
function keepTextFit(el) {
var f = el[0].getAttribute("data-font");
for (var i = 0; i < el.length; i++) {
var c = el[i].textContent.split('').length;
el[i].style.cssText =
'font-size: calc(' + f + ' / ' + c + ')';
}
}
})(document,null);
h2 {
display: inline-block;
font-family: sans-serif;
text-transform: uppercase;
white-space: nowrap;
overflow-x: hidden;
margin: 0;
}
<h2 data-font="130vw">Nouvelle Zelande</h2>
<h2>Australia</h2>
<h2>Cyprus</h2>
Note, since resize events can fire at a high rate, the throttler is used to reduced the rate so the handler doesn't execute expensive operations such as DOM modifications too often.
If you want to make a perfect fit, check this post: fit-text-perfectly-inside-a-div
If you are looking to use a plugin there's
http://fittextjs.com/
wich can do that for you

HTML CSS Styling for Hebrew Niqqud

I'd like to style niqqud characters inside html differently than the letter.
Suppose I'd like to have Hebrew letter Bet black while Dagesh in it green.
How can this be made in html+css?
This doesn't do the task:
<div style = "font-size: 500%">
<span style = "color: black">ב</span><span style = "color: red">ּ</span>
</div>
It results in : http://jsfiddle.net/nv7ja459
(link with bigger font: http://jsfiddle.net/nv7ja459/1/)
So the dagesh is no more inside the letter.
Link to screenshot https://drive.google.com/file/d/0B4SYIrNV4hXYZ0ZyWXZnZWg4OGc/view?usp=sharing
The main issue here is that when you wrap the dagesh, niqqud or other diacritic in a span (to style it) - the browser no longer knows which consonant it was attached to.
Because of this, it cannot position it correctly. For example, vav is much narrower than shin. Let's say the browser positions qamats 5px to the right when attached to a vav and 10px to the right when attached to a shin. When you wrap qamats in a span the browser does not know which consonant it is attached to and therefore does not know how far to move it to the right. So it just gives up and doesn't move it to the right at all. Hence, why, when you wrap vowels, etc in a span the position is messed up.
You can color different letters differently without messing up positioning as long as each consonant is inside the same span as any any attached vowels / diacritics. You can also color the first letter (including its vowel) differently using the ::first-letter CSS selector.
Finally, you can use a layering approach as discussed in Jukka's answer when a consonant and its attached diacritics need to be colored differently. I have added a more thorough example to my code snippet.
I tried with SVGs to see if they offered a better solution. But SVG's suffer from the exact same problem.
PS Can you spot the deliberate spelling mistake in shalom? :D (AKA I cannot be bothered to fix it)
.example-one {
font-size: 100px;
}
.example-one .one {
color: red;
}
.example-one .two {
color: green;
}
.example-one .three {
color: pink;
}
.example-one .four {
color: blue;
}
.example-two {
font-size: 100px;
}
.example-two::first-letter {
color: orange;
}
.example-three-a, .example-three-b {
font-size: 100px;
position: absolute;
right: 0;
}
.example-three-a {
color: red;
z-index: 1;
}
.example-three-b {
color: green;
}
<div class="example-one" dir="rtl">
<span class="one">שָׁ</span><span class="two">ל</span><span class="three">וּ</span><span class="four">ם</span>
</div>
<div class="example-two" dir="rtl">שָׁלוּם</div>
<div class="example-three-a" dir="rtl">שלום</div>
<div class="example-three-b" dir="rtl">שָׁלוּם</div>
The example is displayed in different ways in different browsers, depending on many things including the font(s) used. For example, in my test on Win 7, Firefox shows a bet with dagesh in all black, whereas Chrome and IE show a black bet with a red dagesh that is badly or very badly displaced.
There is no reason why your approach would not work. Neither is there any specification requiring that it should work. Browsers (and other rendering software) can display the combination using a single precomposed glyph, in which case the glyph will obviously be in one color. They can also display the base character and the diacritic mark separately; this could result in the desired rendering, but positioning a diacritic mark is a real challenge, and browsers often fail.
This means that you need a trick of some kind.
A relatively simple trick is to have content that has both the base character (bet in this case) and a combination of the base character and a diacritic mark (here dagesh), set different colors on them, and superimpose them so that the base character is topmost. The main objection is logical: the document then contains the base character appearing with no reason (except the visual rendering). Assuming this is acceptable, here’s how to do it:
[Code edited Dec 16, 2020, to make both of the inner elements absolutely positioned.]
<style>
.colcomb { position: relative }
.colcomb .base, .colcomb .combined { position: absolute; left: 0; top: 0; }
.colcomb .base { z-index: 200; }
.colcomb .combined { z-index: 100; }
.colcomb .combined { color: red; }
</style>
<div style = "font-size: 500%">
<span class="colcomb">
<span class="base">ב</span>
<span class="combined">בּ</span>
</span>
</div>
This will work:
<font color='green' size='12'>ּ</font><font color='black' size='12'>ב</font>
tested on chrome and firefox, if its red you want the dot instead of green just change the green to red
the font size is at 12 just to make it visible, you can remove that also
http://i.imgur.com/smkx3MN.png - Screenshot for how it looks for me

Div smart width

see fiddle
html
<div class="e" style="left:5px;top:5px;">aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbb</div>
<div class="e" style="left:5px;top:100px;">aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</div>​
css
.e {
font-size: 10px;
font-family: arial;
background-color: yellow;
position: absolute;
max-width: 300px;
}
you will notice the 2nd div fits the size of the content exactly, but on the first div there's a bunch of empty space to the right of the a's and b's. This is because the div hit its max width of 300px and then wrapped the b's to a 2nd line. The wrapping is good, but then I would expect the div to then shrink back down to the width of the a's so that there's no empty space to the right.
Is it possible to get it to do this?
Tested in Chrome and FF.
​
you may avoid handling the width, as my understanding is that you're looking for a way to break the text in a satisfying manner.
use word-break: break-all, to wrap the text whenever it hits the container's edge.
Example:
demo on jsFiddle
Reference:
the CSS3 word-break property on Mozilla Developer Network
To shrink a div (or any element) to the size of its text content, you can use JavaScript to get a range that contains its contents and get the size of the range using range.getBoundingClientRect():
function sizeElementToContents(el) {
var range = document.createRange();
range.selectNodeContents(el);
var textRect = range.getBoundingClientRect();
el.style.width = textRect.width + "px";
}
But, of course, that only works with Modern browsers. IE8 and IE7 have different methods for working with ranges. Actually, IE7 automatically handles max-width the way you want it to, but when our IE8 code is run to re-size the divs on IE7, it shrinks the divs to 0. To avoid writing code for specific browser versions, this code runs on IE7 and IE8, but includes a little extra logic so that it works on both browser versions:
function sizeElementToContents(el) {
var range, width;
if (document.createRange) {
range = document.createRange();
range.selectNodeContents(el);
width = range.getBoundingClientRect().width;
}
else {
range = document.body.createTextRange();
range.moveToElementText(el);
range.moveStart("character", 1);
width = range.boundingWidth;
var height = range.boundingHeight;
range.collapse();
range.moveEnd("character", 1);
if (range.boundingHeight == height) {
return; // text doesn't wrap, so don't resize
}
}
el.style.width = width + "px";
}
Working demo: http://jsfiddle.net/gilly3/HZRFb/
Forking from Biziclop's solution:
.e {
font-size: 10px;
font-family: arial;
background-color: yellow;
position: absolute;
max-width: 300px;
margin-right:100%;
}
Link to the Fiddle.
Don't ask me why it works, it might break tomorrow, but adding some extreme margin-right seems to solve the problem:
.e {
margin-right: 9999px;
}
See http://jsfiddle.net/2cTga/1/