How to ensure that only the intended text can be selected? - css

My goal is to create rows of lines (with line numbers) such that:
line numbers line up with the appropriate line even if each "line" exceeds its parent element's width and wraps to a new physical line
only the text is selected, not the line numbers
whitespace is preserved both within and before each line
I've got a snippet that accomplishes some of these goals on specific browsers:
.code { counter-reset: line; }
.line { counter-increment: line; white-space: pre-wrap; }
.line:before { content: counter(line) " - "; }
<div class="code">
<div class="line">Line 1</div>
<div class="line"> Line 2</div>
<div class="line">Line 3</div>
</div>
Here's the problem:
everything works in Chromium
everything works in Firefox except for the third goal - whitespace at the beginning of the line is truncated for some reason
everything works in IE except for the second goal - line numbers are included in the selection
(I don't have access to a Mac, so I have no idea about Safari. I'm too lazy to test Opera.)
Is there a way to make this work in all common browsers?

Short answer: you probably can't get IE & FF to behave the same as Chrome, or even each other.
Opera is essentially Chrome at this point (even using Google's engine, not Webkit), so consider it tested if Chrome works.
Longer info i found...
This question reveals two bad behaviors from the other two browsers:
MSIE:
It's natively selecting generated content in the ::before pseudo-element. By my understanding (and that of the other browsers) the rules say it's NOT supposed to: it's not part of the DOM, it's generated by CSS.
There's an HTML attribute unselectable="on" which generally gets IE to stop selecting text. However, if you open this question in IE, you'll see you can select the "unselectable" text if you start on the block before & continue dragging past it, or right-click inside the box & choose "Select All" then Copy. So it's not even mildly bullet-proof. Besides, an attribute doesn't help you protect a pseudo-element.
There's CSS user-select:none (and vendor prefixes), but when trying it in a JSFiddle with IE, i saw no effect.
The last option for IE is Javascript: onselectstart="return false", which is the perfect chainsaw for clipping coupons.
Firefox:
It doesn't copy formatting, even when CSS dictates the display should show white-space. i saw a Bugzilla issue on this dating back to 2001(?!??!!), with comments over the last 14 years arguing why pre white-space should be preserved to the clipboard, but Mozilla mostly ignoring it.
i have an add-on to copy as plain-text, but i believe it strips markup from a Firefox-originated copy, so the add-on doesn't receive the white-space either.
There's an about:config option that tells Firefox whether to take the space next to a word when double-clicking to select it, but it's utterly useless here. i mention it only to exclude it. :(

Related

Chrome/Edge and Firefox wrap long hyperlinks differently. Why?

I have noticed that Chrome/Edge and Firefox wrap long hyperlinks differently. Firefox breaks them on the slash / character, whereas Chrome/Edge doesn't consider the slash / character special.
Is there some description about this behavior? That is:
Why is it different?
Which one is really correct?
div {
background: silver;
font-family: 'Courier New';
font-size: 16px;
max-width: 18ch; width: min-content;
}
a.bare {
overflow-wrap: break-word;
}
<div>
<p>see <a href="..." class="bare">
http://site.web/section/paaaaaaaaaaaaaaaaaaaaage.html</a></p>
</div>
overflow-wrap: break-word:
Firefox:
Chrome and Edge:
overflow-wrap: initial /* normal */:
Firefox:
Chrome and Edge:
What you're looking for is the line-break property. It specifies what characters are allowed to have line-breaks and which do not, but it's not precise. To summarize it gives some specific rules for certain characters in certain languages, but it does not specify the situation that you have asked about. It doesn't specify anything about the slash character. So since the CSS standard doesn't specify what is right, neither of these implementations are wrong.
The default value for the line-break property is auto which does the following:
The UA determines the set of line-breaking restrictions to use, and it may vary the restrictions based on the length of the line; e.g., use a less restrictive set of line-break rules for short lines.
There is another standard, the unicode line breaking algorithm, which is far more specific and it includes a little thing which says that slashes should provide a line breaking opportunity after them for the exact situation you have inquired about. URLs being as frequent as they are on the web, it makes sense to be able to do line breaks in them at slashes, and so it's in that standard.
According to the firefox source code as best as I could find, it says it follows that unicode standard, but that's not what it appears to be doing according to your example, instead the slash in firefox seems to provide a line breaking opportunity before it. Maybe someone with more in depth knowledge of the firefox source code could explain why it does that?
I'm not sure about the chrome line breaking algorithm because it's a lot more difficult to search the source code, but I imagine the developers decided that the '/' character doesn't deserve a line break under those circumstances based on the broad definition of 'auto' in the css spec.
break-word: If the exceeds the width of the line, then the word is forced to splite and wrap.
You can refer to this doc for some explanations of the value of the overflow-wrap property, and I think the results shown in Edge/Chrome are correct.
In your example, the text is treated as a single word, so when setting the attribure value to normal or initial, it will not be forced to splite. You can add a few spaces to the text and you will find out this.

Interaction of pseudo-elements with :not pseudo-class

Short version:
Using Chrome, I noticed that the selector
#main-content p:first-of-type::first-letter:not(.subhead) { ... }
seems to fail to apply first-letter styles regardless of the class of the paragraph, but if I place the :not pseudo-class earlier, as in either of
#main-content p:first-of-type:not(.subhead)::first-letter { ... }
#main-content p:not(.subhead):first-of-type::first-letter { ... }
then it works as I would expect. Is this an issue in Chrome, or is there some aspect of CSS selectors which explains this, that I just don't understand?
(Additionally, if it's not just an issue in Chrome, can someone suggest a better title for this question?)
Long version:
The short version seems like a good self-contained question to lead with, but there's some more complicated background info which might have its own interesting ramifications. (And which might warrant its own separate question, or be another bug. Help very much appreciated???)
Originally I was trying to apply initial drop-caps to the first paragraph in a div and any subsequent paragraphs that immediately followed a horizontal rule, unless they were part of a subheading. Here's a highly-abbreviated mock of the page structure:
<div id="main-content">
<section class="panel">
<header>
<h1>Welcome Friend!</h1>
<p class="subhead">This is a subheader! (0)</p>
</header>
<div class="customhr"></div> <!-- consciously NOT an hr tag -->
<p>Here's a paragraph (1)</p>
<hr>
<p>First paragraph of a new content section (2)</p>
<p>And another paragraph. (3)</p>
</section>
</div>
And the extremely-abbreviated CSS:
#main-content p:first-of-type::first-letter,
#main-content hr + p::first-letter {
/* Drop-cap styles */
}
.subhead::before {
/* Indent sub-headings with a cute fleuron */
content: "\00a0\00a0\2767\00a0";
}
While testing in Firefox, this resulted in paragraphs (1) and (2) having drop-caps, which is what I desired. In Chrome however, the fleuron from the ::before rule for paragraph (0) also has the drop-cap styles applied. I'm fairly confident Firefox is in error here, as :first-letter should match text from the ::before pseudo-element if it is present, but instead the ::before causes :first-letter not to match anything.
In trying to fix it for Chrome, I added the :not pseudo-class to the end of the first selector (see the first CSS rule in the short version). In Firefox, ironically, this produces the desired effect just as before, but in Chrome, this now caused paragraph (1) to no longer have the drop-cap styles applied. That, I have no explanation for, but I determined it has nothing to do with the .subhead element; Chrome apparently just never applies the rule, and paragraphs following a horizontal rule are the only ones that get styled. But if I move the :not pseudo-class earlier in the rule, as in the two alternatives I gave in the short version, it once again works as expected.
I feel as though I somehow simultaneously stumbled onto two different bugs in Firefox and Chrome, but the use of pseudo-classes and pseudo-elements is slightly arcane to me and I'm not fully confident with them. So I'm curious a) whether there's anything in-spec that would explain this CSS behavior in either browser as being correct, and b) if not, whether these are already known bugs with consistent behavior that has been documented somewhere. [And c) if not, let me know whether you plan to report said bugs so that I don't accidentally file a duplicate.]
::first-letter and ::after/::before do interact in Firefox, but only when the first character is a letter, a number or some punctuation. Other symbols (like the fleuron) seem to be ignored by ::first-letter whether ::after/::before are present or not (JSFiddle).
Chrome is IMO right regarding the relationship between :not() and pseudo-elements. The documentation says that a pseudo-element must always appear at the end (that's why the first selector from the short version doesn't work):
Only one pseudo-element may appear per selector, and if present it
must appear after the sequence of simple selectors that represents the
subjects of the selector

CSS - Internet Explorer and the <main> tag background

I have a pretty simple layout which renders fine in both Firefox and Chrome, but Internet Explorer (version 11) seems to be unable to render any kind of background color for the <main> element.
I have the <main> element as a child of the <body> element and neither background or background-color seem to make any difference. <main> will always have the same background as <body>. I haven't found anything that says whether or not this is a bug in IE.
Check out this jsfiddle using Internet Explorer to see what I mean.
Obviously, I could just replace <main> with <div id="main"> and update my CSS selectors but I want to understand why this is happening.
IE11 does not support the <main> element natively. You can introduce support for it by either using a script like Modernizr, or a single harmless line of JS:
document.createElement('main');
The element will not be inserted in the DOM, but it will now be recognized as a proper element by IE. After this, it still does not have proper styling. Add the following to your CSS:
main {
display:block;
}
And all will be fine. The reason you currently see it as not getting any content because IE does not add it to the box model without these 2 steps, and as such it gets no 'layout' or 'size'. It's just invisible, that's why you see the body. It does contain elements, which get rendered (sort of) correctly based on the top left coordinate of the <main> element.
Simple: The <main> tag is not supported in IE11.

How to make CSS insert line breaks without white space?

I need help with a bit of CSS I'm trying to use. I'm testing with Chrome, but I need a solution that's not browser-specific.
I have a page divided into two iframes: a narrow one on the left that contains a table of contents, and a wide one on the right that displays a selected page. I want to display the URL of the selected page at the bottom of the left-hand frame. If it's too long to fit in the frame (as it usually is), it should wrap to a second line, and the first line should move up so that the last line remains bottom-aligned.
The structure of the page in the table of contents iframe looks like this:
<body>
<div>
<script...> <!--JavaScript that generates the table of contents--> </script>
</div>
<div id='showPageUrl' style="height:auto;position:absolute;bottom:0"></div>
<script...> showURL(document.URL) </script>
</body>
The following function is executed by the JavaScript code that loads pages (from an onclick event), and also by HTML that loads the initial page (above).
function showUrl(url) {
var sel = document.getElementById('showPageUrl');
if (sel!==null) {
sel.innerHTML = url;
}
}
The problem: if the URL is too long to fit on one line it doesn't wrap, because it contains no whitespace characters to wrap at. Instead the frame sprouts a horizontal scroll bar. If I replace the URL with a piece of text that contains whitespace, the text breaks at a whitespace character and displays properly.
I've looked for a CSS property to make the URL break wherever it has to, but I can't find anything. All the line break controls seem to assume there's whitespace and change how the rendering engine treats it.
What must I do to make a URL (with no whitespace) break properly at the end of a line?
You're looking for the overflow-wrap CSS property (legacy word-wrap):
.example {
word-wrap: break-word;
overflow-wrap: break-word;
}
Documentation: http://www.w3.org/TR/css3-text/#overflow-wrap
Browser support: http://caniuse.com/#search=overflow-wrap
DEMO: http://jsfiddle.net/aueL3/2/
#showPageUrl { word-break: break-all; }
For reference: http://www.w3schools.com/cssref/css3_pr_word-break.asp
This is not really a CSS issue. To specify allowed direct break points, you can use the <wbr> tag or the zero-width space character ​. You need to decide on the principles of breaking; there are various standards and practices on where a URL can be broken.
Primarily, don’t put URLs into textual content. They should appear in attributes, like href.
You can use this CSS to break the URL anywhere that is going to exceed the parent's width.
Here is the CSS:
.text {
position: absolute;
display: block;
width: 100%;
bottom: 0;
white-space: pre-wrap; /* CSS3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}
white-space and word-wrap code taken from How do I wrap text with no whitespace inside a <td>?
Finally, a JSFiddle: Demo
Related Question: SO Question
zero-width space character ​ is perfect for text, but urls are likely to be copied, and when pasted the space will be right there to break them. Since it is invisible, people won't understand what's wrong and will think the url is not working. Since support for wbr is not universal, for a similar issue I split the url in two spans marked with display:inline-block. They split just like an invisible space and don't mess copy & paste (is important to avoid spaces or newlines between them in the html or a space will be visible).
<span style="display:inline-block">firstpartoflongurl</span><span style="display:inline-block">seconfpartofit</span>
*I know this is too old to be useful to the original author, but may help others looking for the same thing
Try to use the following line to insert new line by replacing white spaces using css.
white-space: "pre-line"

Is there a CSS workaround for Firefox' bug: inline-block + first-letter with changed size

It's better to see a bug for yourself in Firefox: http://jsfiddle.net/kizu/btdVd/
The picture, showing the bug:
And the bug filled in 2007 on bugzilla.
The bug appears when you're adding ::first-letter pseudo-element with display: inline-block, and then change the font-size of this first-letter.
More letters in a word after the first: more extra space added (or subtracted — if the font-size is lesser than block's).
Adding float: left to the first-letter inverts the bug: with bigger font-size the width of inline-block shrinks.
So, the question: is there any CSS-only workaround for this bug? It's somewhat killing me.
I've found that triggering reflow on the whole page (or any block with a problem) fixes the problem, so I've found a way to trigger it on every such block with one-time CSS animation: http://jsfiddle.net/kizu/btdVd/23/
Still, while this fix have no downsides in rendering, it have some other ones:
it would work only for Fx5+ (that supports animations);
it still flashes the original bug for a few ms, so it's maybe somewhat blinky.
So, it's not an ideal solution, but would somewhat help when Fx4- would be outdated. Of course, you can trigger such fix onload with JS, but it's not that nice.
I don't think there's a good solution.
I have come up with a flaky solution for you though:
.test:first-letter {
font-size: 2em;
letter-spacing: -0.225em;
}
Add the letter-spacing style to the :first-letter selector in your Fiddle, and you'll find the blocks go back to roughly the right size.
Explanation:
Basically, the bug is being caused by the whole block taking its size from the font specified in the first-letter.
What I'm doing here with the letter-spacing is trying to adjust the size of this font, without affecting it's physical appearance. Adjusting the letter spacing in this way in normal text would result in the letters overlapping each other by .225 of a character width on either side.
I was initially hoping that a value of -0.25 would be sufficient -- ie a quarter of a character on each side would reduce the width of each character by half, which would be what we want because the first letter is font-size:2em, so it's twice as big.
However, the calculation isn't quite as clean as that, because the first and last characters in the string would only be overlapped on one side each, and because the first letter does in fact want to be double width, even if the rest of the characters don't.
All of this means that the exact letter-spacing value required to counter-act the bug will vary depending on how long the text, as well as the font sizes of the original text and the first letter. This is why I had to experiment a bit with the value of the letter spacing to get it working, and also explains why I couldn't get quite a perfect fit on all the text rows in your Fiddle. I would have needed a slightly different value for each block.
The value of -0.225 seems to be about the closest I can get to it being right for all your examples, but in practice you'll need to adjust it to suit your site.
Don't forget also that this is a Firefox bug, and therefore you'll need to write it in as a browser-specific hack of some sort. And be careful to keep an eye on the Firefox bug report you linked; when it does get fixed, you'll need to work out a way to keep your hack in place for users of old versions, but remove it for users with the fix.
[EDIT]
The only other working solution I've come up with is simply not to use the features which trigger the bug. ie drop the :first-letter selector, and use a separate <span> for the first letter of your text if you want to style it differently.
This is the obvious answer really, and would of course solve the problem (and would also mean that your styled first letter works in older browsers), but it would not be ideal from a semantic perspective, and more importantly doesn't actually answer the question, which is why I didn't offer it as a solution in my original answer.
I have been trying to find alternative work around for the bug as well, but the options are limited, and nothing I've tried has given as good results as my initial suggestion.
I tried a hack of making the :first-letter invisible, and using :before to display the big leading capital letter. However, this didn't work for me. I didn't linger on it too long so you may be able to get it working, but there is a problem with it in that you'd have to define the leading letter in your CSS, which wouldn't be ideal.
Another possible solution is to use the CSS font-stretch: condensed; property on the :first-letter. This would reduce the width of the first letter back to 1em, and thus resolve the width issue of the rest of the text. The down sides of this, however, are that firstly it would make the leading letter look squashed, which is probably not what you want, and secondly this style only works for fonts which support the condensed property. It turns out that this isn't well supported by the standard fonts, so may not be workable for you.
In the end, the original letter-spacing solution is still the only way I've found to really work around the bug.
This bug still exists, but some of the fixes don't work anymore. Even after triggering a reflow with an animation, the inline-block returned to the same size for me. I couldn't use the letter-spacing trick because I am already using it on the first letter, that is what is causing the problem for me. I solved the problem by adding this to the CSS for the affected selector:
-moz-padding-end: *number of pixels to compensate*;
At the moment, moz-padding-end seems to be specific to Firefox, and it can add or remove width to the end of the inline-block. Because this is a Firefox specific bug, that did the trick for me.
I know this thread is quite old now, but apparently this bug has not been fixed yet.
Using animation does work but there is a noticeable FOUT (Flash Of Unstyled Text). I was able to work around the problem by wrapping the first-letter in a span. This does work around both the sizing issue and the FOUT, it does add extra elements to the markup, so it depends on the needs of your site/application if this is the best fit.
.test {
background: rgba(0,0,0,0.15); /* For visualisation */
display: inline-block;
}
.test:first-letter {
font-size: 2em;
}
.test2:first-letter {
float: left;
}
.test3:first-letter {
font-size: .5em;
}
<h1>Inline-block with bigger first-letter</h1>
<span class="test">Broken</span>
<br><br>
<span class="test"><span>F</span>ixed</span>
<h1>+ floating to first-letter</h1>
<span class="test test2">Broken</span>
<br><br>
<span class="test test2"><span>F</span>ixed</span>
<h1>small size for first-letter</h1>
<span class="test test3">Broken</span>
<br><br>
<span class="test test3"><span>F</span>ixed</span>
<h1>small size, floating first-letter</h1>
<span class="test test2 test3">Broken</span>
<br><br>
<span class="test test2 test3"><span>F</span>ixed</span>
As of 2023, this is still happening in Firefox.
This is my solution using SASS, but you can see how to make it bare CSS:
txt-brand {
display:inline-block;
}
///Firefox only ///
#-moz-document url-prefix() {
margin-right: .1em;
white-space: nowrap;
&::after {
content: '\00a0';
}
}
}
.txt-brand::first-letter {
letter-spacing: -.11em;
}

Resources