I am trying to format quotes the old-fashioned way. — Is there any way to go from this input:
<p>He argued that <q>the King by his proclamation or other ways cannot change any part of the common law, or statute law, or the customs of the realm</q> and concluded that…</p>
to this output (as displayed on the browser):
He argued that “the King by his procla-
“ mation or other ways cannot change any
“ part of the common law, or statute law,
“ or the customs of the realm” and con-
cluded that…
In the example above, hyphenation, alignment and line length are set arbitrarily and for illustration purpose. They aren’t of any concern.
I would like that, when a line breaks inside the <q>, each consequent lines (that are in the <q>), when displayed, be preceded by a quotation mark (so as the reader would visually isolate the quote). It is an old-fashioned way of formatting indirect speech.
The best I could come up with so far is an unsatisfactory pseudo-workaround using <blockquote> combined with text-shadow in CSS:
* {font-family: monospace; line-height: 1.4em; width: 25em; margin: 0;}
blockquote { position: relative; padding-left: 1.2em; text-indent: -1.2em; overflow-y: hidden;}
blockquote::before {content:"“"; position: absolute; top: 1.4em; left: 1.2em; text-shadow: 0 1.4em 0 #000, 0 2.8em 0 #000, 0 4.2em 0 #000;}
<p>He argued that <blockquote>“the King by his proclamation or other ways cannot change any part of the common law, or statute law, or the customs of the realm”</blockquote> and concluded that…</p>
(Which may vaguely resemble what I wanted (kinda), but ultimately does not work given the block nature of <blockquote>; furthermore, when set inline, the desired effect of the quotation marks being set to the left is lost.)
Edit: acknowledging answers.
Obviously, we are going the JS route. I’ve considered:
Find browser-computed line breaks, and replace each one with "<br>“ "(eventually leading to multiple issues in justified texts—therefore not ideal);
#A-Haworth suggestion of “[a] method to find the line breaks [with] a dummy element, gradually add[ing] the text and spot[ting] when it gets higher.”
But I just don’t know how to do any of these things. Also, there must be a solution I haven’t thought of.
Any idea?
Last Edit (Feb 16, 2022)
No one, really?
It probably can't be done in an easy way. I tried to write a small function that might help you.
The solution needs to be finalized, but you can take the basic idea. if you need a dynamic solution, use resize observer, as indicated in the example.
function quote(el, leftPad = 16) {
const txt = el.textContent.trim().split(" ");
const ctx = document.createElement("canvas").getContext("2d");
const styleList = getComputedStyle(el);
ctx.font = `${styleList.fontWeight} ${styleList.fontSize} "${styleList.fontFamily}"`;
const maxWidth = el.clientWidth;
let paragraph = [];
let line = "";
let lineWidth = 0;
let blockquote = false;
const symbolMap = new Map();
let firstLineBreak = false;
let quoteRowCount = 0;
const getWordLength = (word) => {
let wordWidth = 0;
for (let i = 0; i < word.length; i++) {
if (!symbolMap.has(word[i])) {
symbolMap.set(word[i], ctx.measureText(word[i]).width);
}
wordWidth += symbolMap.get(word[i]);
}
return wordWidth;
};
for (let i = 0; i < txt.length; i++) {
const word = txt[i] + " ";
const width = getWordLength(word);
lineWidth += width;
if (txt[i].search('"') >= 0) blockquote = !blockquote;
if (lineWidth > maxWidth) {
paragraph.push(line);
if (!firstLineBreak && blockquote) {
line = '<br><span class="newline"></span>';
firstLineBreak = true;
quoteRowCount++;
} else {
if (blockquote) quoteRowCount++;
line = "";
}
lineWidth = leftPad + width;
}
line += word;
}
if (line.trim() !== "") paragraph.push(line);
el.innerHTML = paragraph.join("<wbr>");
const newLineElement = el.querySelector(".newline");
if (newLineElement) newLineElement.style.height = `${quoteRowCount}em`;
}
const el = document.querySelector("p");
quote(el);
// or if you need ResizeObserver =>
//new ResizeObserver(() => quote(el)).observe(el);
p {
width: 220px;
font-size: 16px;
font-family: Arial;
padding: 0;
box-sizing: content-box;
}
.newline {
display: block;
width: 1em;
padding-right: 1em;
float: left;
overflow: hidden;
box-sizing: border-box;
word-break: break-word;
}
.newline:before {
content: '""""""""""""""""""""""""';
}
<p>
He argued that "the King by his proclamation or other ways cannot change any part of the common law, or statute law, or the customs of the realm" and concluded that…
</p>
Interesting question - how much can be done by CSS?
While I think for a totally general answer will need JS - slowly adding text to the paragraph and looking to see whether the height has changed or not - here's a snippet that does it mainly by CSS using pseudo element. However, as the browser uses the pseudo element to put in the quotation marks itself, these have to be placed in the text if they are still required.
It requires a way of getting the text that is in the paragraph before the q element into the content of the pseudo element also. Here this is done with a data attribute. Space is reserved for the column of double quotes by doing a text-indent on the paragraph.
So, not a totally general solution, but may point to a way of making it general using a bit of JS to do the content setting up initially.
p {
margin: 50px;
padding-left: 20px;
position: relative;
text-indent: -10px;
}
q {
position: relative;
}
q::before {
content: attr(data-start) '\a " \a " \a " \a " \a " \a " \a " \a " \a " \a " \a " \a " \a " \a " \a " ';
position: absolute;
top: 0;
left: 0;
transform: translateX(-100%);
height: 100%;
width: auto;
z-index: -1;
text-indent: 0;
white-space: pre;
padding-left: 20px;
overflow: hidden;
}
<p>He argued that <q data-start="He argued that ">"the King by his proclamation or other ways cannot change any part of the common law, or statute law, or the customs of the realm"</q> and concluded that…<br>some more text here for test</p>
Related
I have a block with a few inline-blocks inside. These all have varying heights and widths. I have line wrap enabled.
What I'm trying to do is if I have a reference to one of the inline-blocks to
find out what line it is in and
how high that line is. (I can't change the HTML)
2 has the obvious solution of getting the bounding boxes of all blocks in the line and using the highest height as the height of the line. Here I'm just wondering if there is a more performant solution for that.
My main interest lies in a). My idea of an incredibly imperformant solution would be doing b) for all lines in the parent block and then comparing the offset-top of the searched element to the line-heights. I was hoping maybe someone can come up with a better idea?
edit:
relevant part of the html, css and js i'm using:
<div>
<div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div>
</div>
div {
display: block;
background-color: yellow;
height: 600px;
width: 2000px;
}
div > div {
display: inline-block;
padding: 10px;
margin: 5px;
}
document.addEventListener("DOMContentLoaded", function() {
const nodes = document.querySelectorAll("div > div");
for(let i = 0; i < nodes.length; i++) {
nodes[i].style.width = `${5 + Math.random() * 200}px`;
nodes[i].style.height = `${5 + Math.random() * 200}px`;
nodes[i].style.backgroundColor = `hsl(${(Math.random() * 360)}, 100%, 50%)`;
}
const nodeiwanttogetdescribeddataof = nodes[4];
//code to get the data
});
i did not implement the ideas described as i was hoping to not need to. the purpose of the question was to find out whether there is some easy way to do what i need, for example (pseudocode) let theline = node.line.
I need to mix units in a dynamically generated div, but this doesn't render (ie left is still the default after that line is executed).
Any help appreciated.
var tab3 = document.createElement('div');
tab3.className = 'tab';
tab3.style.display = 'none';
tab2.style.left = 'calc(~"200px+40vw")px';
You've got several mistakes in your calc expression:
calc(~"200px+40vw")px` -> `calc(200px + 40vw)
According to #JohnWeisz's comment - calc(~"200px+40vw")px is a Less expression, ~"..." is used to prevent Less from processing the addition operation from the source stylesheet files, but that's both invalid syntax and unnecessary from CSS string context.
#G-Cyr adds that white space is required on both sides of the + and - operators. The * and / operators can be used without white space around them.
Example:
var tab3 = document.createElement('div');
tab3.className = 'tab';
tab3.style.left = 'calc(20px + 40vw)';
document.body.append(tab3);
.tab {
position: absolute;
width: 20vw;
height: 20vh;
background: red;
}
If a character is entered in a text field and the currently applied font does not support that character, a backup font is used for that character. Is it possible to tell via Javascript or some other means when this is happening? Trying to create a script which alerts the user if a certain character is not supported by the font. Thanks for your help!
This sounds like a job for something like fontkit.js or opentype.js, or even Font.js, all of which can test fonts for glyph support. System fonts are not covered this way (Font.js might work, it'll probably report a width 0 for the tested glyph) but then these are typically the "web safe" fonts, of which we already know exactly which glyphs are supported because everyone uses the same ones. For testing "holes" in a custom font, though, any of the previously mentioned three should already do what you intended to implement.
Checking the width of the character in the font, and comparing it with the fallback, you can infer if it is using the fallback font. This test is probably not 100% reliable, but you can do something like:
var text = $(".main-text").html();
var chars = text.split("");
for(var i = 0, len = chars.length; i < len; i++){
var str = chars[i]; str+=str;str+=str;str+=str;
$("#test1, #test2").html(str);
var w1 = $("#test1").width();
var w2 = $("#test2").width();
if(w1 == w2 && w1 != 0){
alert("char not supported " + chars[i]);
}
}
.main-text {
width: 50%;
height: 300px;
font-family: Impact, sans-serif;
font-size: 16px;
}
#test1, #test2 {
position: fixed;
top: -100px;
font-size: 16px;
}
#test1 {
font-family: Impact, sans-serif;
}
#test2 {
font-family: sans-serif;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea class="main-text">
Serif Font
This E is not supported: ể
</textarea>
<span id="test1"></span>
<span id="test2"></span>
jsfiddle
I am trying to calculate the elapsed time an element is painted onto the DOM from the start time of the script or if the specific element was even painted at all. I am inserting a background gradient to the HTML, and then using javascript to create random (clouds, which are just large periods with a text shadow) in multiple places across the screen (some negative, some positive, some within scope, some outside of scope).
Currently my code looks like this:
<html>
<head>
<style>
.container {
border: 1px solid #3b599e;
overflow: hidden;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#315d8c', endColorstr='#84aace'); /* for IE */
background: -webkit-gradient(linear, left top, left bottom, from(#315d8c), to(#84aace)); /* for webkit browsers */
background: -moz-linear-gradient(top, #315d8c, #84aace); /* for firefox 3.6+ */
}
.cloud {
color: #fff;
position: relative;
font: 100% "Times New Roman", Times, serif;
text-shadow: 0px 0px 10px #fff;
line-height: 0;
}
</style>
<script type="text/javascript">
function cloud(){
var b1 = "<div class=\"cloud\" style=\"font-size: ";
var b2 = "px; position: absolute; top: ";
document.write(b1+"300px; width: 300px; height: 300"+b2+"34px; left: 28px;\">.<\/div>");
document.write(b1+"300px; width: 300px; height: 300"+b2+"46px; left: 10px;\">.<\/div>");
document.write(b1+"300px; width: 300px; height: 300"+b2+"46px; left: 50px;\">.<\/div>");
document.write(b1+"400px; width: 400px; height: 400"+b2+"24px; left: 20px;\">.<\/div>");
}
function clouds(){
var top = ['-80','80','240','400'];
var left = -10;
var a1 = "<div style=\"position: relative; top: ";
var a2 = "px; left: ";
var a3 = "px;\"><script type=\"text/javascript\">cloud();<\/script><\/div>";
for(i=0; i<8; i++)
{
document.write(a1+top[0]+a2+left+a3);
document.write(a1+top[1]+a2+left+a3);
document.write(a1+top[2]+a2+left+a3);
document.write(a1+top[3]+a2+left+a3);
if(i==4)
{
left = -90;
top = ['0','160','320','480'];
}
else
left += 160;
}
}
</script>
</head>
<body style="margin:0;padding:0;">
<div class="container" style="width: 728px; height: 90px;">
<script>clouds();</script>
</div>
</body>
</html>
I then run this inside of an iframe, trying to detect if the visible elements are being painted first, or if they are being painted in display order (pretty much, is the ad currently being viewed, or is it out of view).
I have not found a solid technique yet that works crossbrowser to detect this. In chrome, I was able to see it work when pasting images, as the visible images got an onload event fired first (even though they were at the end of the DOM), but this wasn't the case for firefox or IE.
I'm really not sure what you're after here. You said: "(pretty much, is the ad currently being viewed, or is it out of view)." But that is a bit cryptic.. What "ad" are you talking about?
In trying to figure out what you were trying to accomplish I pretty much rewrote your script to do exactly the same as before and it does an alert of the elapsed time as well as logs the time for each cloud in the console. It's not a good way to calculate execution time (which is what it seemed to me like you were after..).
NOTE: The only reason I rewrote everything was so I myself could make better sense of it in preparation for a response from you. Once I know exactly what you want I will edit my answer.
<style>
.container {
width: 728px;
height: 90px;
border: 1px solid #3b599e;
overflow: hidden;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#315d8c', endColorstr='#84aace'); /* for IE */
background: -webkit-gradient(linear, left top, left bottom, from(#315d8c), to(#84aace)); /* for webkit browsers */
background: -moz-linear-gradient(top, #315d8c, #84aace); /* for firefox 3.6+ */
}
.cloud {
color: #fff;
position: absolute;
font: 100% "Times New Roman", Times, serif;
text-shadow: 0px 0px 10px #fff;
line-height: 0;
}
.cloud-container {
position: relative;
}
</style>
<div class="container"></div>
<script type="text/javascript">
// for IE
if (!window.console) window.console = {};
if (!window.console.log) window.console.log = function () { };
var pxTop = ['34px', '46px', '46px', '24px'],
pxLeft = ['28px', '10px', '50px', '20px'],
size = ['300px', '300px', '300px', '400px'];
function cloud(callback) {
var df = document.createDocumentFragment(),
node;
for (var i = 0; i < 4; i++) {
node = document.createElement('div');
node.className = 'cloud';
node.style.fontSize = size[i];
node.style.width = size[i];
node.style.height = size[i];
node.style.top = pxTop[i];
node.style.left = pxLeft[i];
node.innerHTML = '.';
df.appendChild(node);
}
callback && callback();
return df;
}
function clouds(containerId, callback) {
var top = ['-80','80','240','400'],
left = -10,
container = document.querySelector(containerId);
container.appendChild(document.createTextNode("\n"));
for (i = 0; i < 8; i++) {
var div = document.createElement('div');
div.className = 'cloud-container';
div.style.top = top[0] + 'px';
div.style.left = left + 'px';
console.log('About to start making a cloud', new Date().getMilliseconds());
div.appendChild(cloud(function() {
console.log('Finished making cloud', new Date().getMilliseconds());
}));
container.appendChild(div);
container.appendChild(document.createTextNode("\n")); // newline for view source
if (i == 4) {
left = -90;
top = ['0','160','320','480'];
} else {
left += 160;
}
}
callback && callback();
}
var start = new Date().getMilliseconds();
clouds('.container', (function() {
var end = new Date().getMilliseconds(),
time = end - this;
alert('Execution time: ' + time);
}).bind(start));
</script>
You should use console API for measuring timings more accurately (and avoid DIY spaghetti):
console.time("Doing stuff");
/*le code*/
console.timeEnd("Doing stuff");
This functionality is available natively in Chrome and Firefox; it can also be polyfilled on other browsers like IE but will not be as precise.
Note that using Date is not appropriate/intended for benchmarking:
Depending on the browser and OS, Date's resolution can be as low as 15 milliseconds.
Date based on system time, isn't ideal. Most systems run a daemon which regularly synchronizes the time... tweaked a few milliseconds every 15-20 minutes. At that rate about 1% of 10 second intervals measured would be inaccurate.
Update:
Hold on, are you trying to measure browser paints' timing and order? These can't be monitored through js (ok, it's possible to have a js API, but ultimately it has to be benchmarked in the browser engine, so no js polyfills).
Rule of thumb is elements will be painted in parse order, with re-paints/re-flows triggered by content styling - like backgrounds being loaded, additional css rules becoming available or content of different size being added into a table column.
Html5rocks on profiling long paint times might get you started in the right direction.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking us to recommend or find a tool, library or favorite off-site resource are off-topic for Stack Overflow as they tend to attract opinionated answers and spam. Instead, describe the problem and what has been done so far to solve it.
Closed 9 years ago.
Improve this question
I am looking for a way to select and display a list of countries, preferably with flags. Any suggestions?
I started of by trying this jQuery plugin http://www.graphicpush.com/website-language-dropdown-with-jquery, but as the list of countries I have is quite large it turned out that the performance was really bad (too many http requests to images). Also the list is bulky when it is larger than 50 elements.
Just wanted to suggest a (imho) smarter way of doing the flags sprite.
The idea is to save the flags in a grid according to the country iso2 code.
1st letter -> vertical position
2nd letter -> horizontal position
Examples (for 16x11px flags + 4x4px spacing):
Austria = AT
A = 1 => vertically 1st row => y = (1-1)*(11+4) = 0
T = 20 => horizontally 20th column => x = (20-1)*(16+4) = 380
United States = US
U = 21 => vertically 21st row => y = (21-1)*(11+4) = 300
S = 19 => horizontally 19th column => x = (19-1)*(16+4) = 360
This way I can calculate the flag location with a very easy function on the client side without the need of 200+ extra style definitions.
Sample jQuery plugin:
(function($) {
// size = flag size + spacing
var default_size = {
w: 20,
h: 15
};
function calcPos(letter, size) {
return -(letter.toLowerCase().charCodeAt(0) - 97) * size;
}
$.fn.setFlagPosition = function(iso, size) {
size || (size = default_size);
return $(this).css('background-position',
[calcPos(iso[1], size.w), 'px ', calcPos(iso[0], size.h), 'px'].join(''));
};
})(jQuery);
Demo Usage:
$('.country i').setFlagPosition('es');
http://jsfiddle.net/roberkules/TxAhb/
And here my flag sprite:
Note from the future: jQuery UI autocomplete now supports custom
rendering by default, see
http://api.jqueryui.com/autocomplete/#method-_renderItem.
It's pretty easy. Things you need:
jQuery UI auto-complete
UI auto-complete html extension
A list of country names/codes
A CSS sprite with all flags
Remember, Google is your friend. Blend the ingredients well, carefully whisk some javascript in, and it's done - in 7 lines of code:
var countries = [["Argentina", "ar"], ...];
var countryNames = countries.map(function(country){
return {
label: '<div class="flag '+country[1].toLowerCase()+'">'+country[0]+'</div>',
value: country[0]
}
});
$('#country').autocomplete({
source: countryNames,
html: true
});
Here's this code in action
As mentioned by commenters, a CSS sprite is the proper solution here. Fortunately, there are many CSS sprites of flags freely available. This one looks pretty good.
We will have to tweak the dropdown code to accomodate that pre-made CSS sprite. I've gone ahead and done that for you. Here's a live demo.
languageswitcher.js
## -44,10 +44,11 ##
source.removeAttr("autocomplete");
var selected = source.find("option:selected");
var options = $("option", source);
- $("#country-select").append('<dl id="target" class="dropdown"></dl>')
- $("#target").append('<dt class="' + selected.val() + '"><span class="flag"></span><em>' + selected.text() + '</em></dt>')
- $("#target").append('<dd><ul></ul></dd>')
+ $("#country-select").append('<dl id="target" class="dropdown f16"></dl>')
+ $("#target").append('<dt><em class="flag ' + selected.val().toLowerCase() + '">' + selected.text() + '</em></dt>');
+ $("#target").append('<dd><ul></ul></dd>');
+ var $drop = $("#target dd ul");
options.each(function(){
- $("#target dd ul").append('<li class="' + $(this).val() + '"><span class="flag"></span><em>' + $(this).text() + '</em></li>');
+ $drop.append('<li><em class="flag ' + $(this).val().toLowerCase() + '">' + $(this).text() + '</em></li>');
});
}
languageswitcher.css
## -45,6 +45,8 ##
.dropdown dd { position: relative; }
+.dropdown ul { max-height:350px; overflow-y:auto; overflow-x:hidden; }
+
.dropdown a {
text-decoration: none;
outline: 0;
## -52,6 +54,7 ##
display: block;
width: 130px;
overflow: hidden;
+ white-space:nowrap;
}
.dropdown dt a {
## -107,23 +110,6 ##
padding: 2px 10px;
}
- .dropdown dd ul li a span,
- .dropdown dt a span {
- float: left;
- width: 16px;
- height: 11px;
- margin: 2px 6px 0 0;
- background-image: url(flags.png);
- background-repeat: no-repeat;
- cursor: pointer;
- }
-
- .us a span { background-position: 0 0 }
- .uk a span { background-position: -16px 0 }
- .fr a span { background-position: -32px 0 }
- .de a span { background-position: -48px 0 }
- .nl a span { background-position: -64px 0 }
-
.dropdown dd ul li a em,
.dropdown dt a em {
font-style: normal;
## -138,3 +124,5 ##
.dropdown dd ul li a:hover { background-color: rgba(255,255,255,.1); }
.dropdown dd ul li a:hover em { color: #fff; }
+
+.flag { padding-left:18px; }
The CSS changes I made were Q&D hacks; you'll probably want to spend some time polishing them. I removed all of the flag-specific stuff from languageswitcher.css since we're using flag16.css.
Also, if the country code doesn't exist in the CSS sprite, the flag shown will default to the
African Union's flag since it is the first image in the sprite. In the demo, several of the countries in my example list don't have a sprite image. Watch out for that.
Here's a file with the list of countries and links to their flags (some of the links might not be working though but most of them are)
Excel File
You can also use flags from http://www.famfamfam.com/lab/icons/flags/ and simply use CSS for positioning the appropriate flag.
----EDITED----
If i need to show flag of my country then i would do mapping from CSS like
<span class='np'></span>
.np {
background: url(./flags_preview_large.png) -172px -397px no-repeat;
width: 14px;
height: 20px;
}