CodeMirror Line-Break doesn't add line number - Angular - css

I'm using code mirror from ngx-codemirror. I want to split the line when it fits to the width of the parent. I have found some solutions to split the like using,
lineWrapping: true
and in styles
.CodeMirror-wrap pre {
word-break: break-word;
}
Using this I was able to split the line but I need to show the line number too.
The line number is not shown for the line that was just split.
This is the stackblitz link to my issue : code-mirror-line-break-issue
Screenshot :
Please help me with this.

This is not feasible using Code Mirror options, as this is something that is a bit counter intuitive that is rarely (ever?) wanted.
Like I said in my comment, say 2 persons discussing on a phone/web chat about a piece of code/json. They will not see the same thing when one mentions a line number to the other if they have different windows/screen sizes
Solution
As a hack, you can create your own elements representing line numbers and place them over the default line numbers.
Here is the stackblitz demo
Note: This a a very basic example. If you change code mirror settings (font size, gutters,...), you might need to tweak the css or do more calculation based on these settings.
component.html
<div class='codeMirrorContainer'>
<ngx-codemirror
#codeMirror
[options]="codeMirrorOptions"
[(ngModel)]="codeObj"
></ngx-codemirror>
<ul class='lineContainer' [style.top.px]="-topPosition">
<li [style.width.px]='lineWidth' *ngFor="let line of lines">{{line}}</li>
</ul>
</div>
component.css
li
{
height: 19px;
list-style: none;
}
.codeMirrorContainer
{
position:relative;
overflow: hidden;
}
.lineContainer
{
position:absolute;
top:0;
left:0;
margin: 0;
padding: 5px 0 0 0;
text-align: center;
}
::ng-deep .CodeMirror-linenumber
{
visibility: hidden; /* Hides default line numbers */
}
component.ts
export class AppComponent
{
#ViewChild('codeMirror') codeMirrorCmpt: CodemirrorComponent;
private lineHeight: number;
public lineWidth;
public topPosition: number;
public lines = [];
codeMirrorOptions: any = ....;
codeObj :any = ...;
constructor(private cdr: ChangeDetectorRef)
{
}
ngAfterViewInit()
{
this.codeMirrorCmpt.codeMirror.on('refresh', () => this.refreshLines());
this.codeMirrorCmpt.codeMirror.on('scroll', () => this.refreshLines());
setTimeout(() => this.refreshLines(), 500)
}
refreshLines()
{
let editor = this.codeMirrorCmpt.codeMirror;
let height = editor.doc.height;
this.lineHeight = editor.display.cachedTextHeight ? editor.display.cachedTextHeight : this.lineHeight;
if (!this.lineHeight)
{
return;
}
let nbLines = Math.round(height / this.lineHeight);
this.lines = Array(nbLines).fill(0).map((v, idx) => idx + 1);
this.lineWidth = editor.display.lineNumWidth;
this.topPosition = document.querySelector('.CodeMirror-scroll').scrollTop;
this.cdr.detectChanges();
}
}

Related

Clarifying the status of custom attributes in HTML Custom Elements in WebComponents

Until recently, whenever I've needed a custom attribute in my HTML, I've always used an HTML5 data-* custom attribute.
Having recently started experimenting with WebComponents and, specifically, Custom Elements, I have started thinking in terms of custom attributes which are not HTML5 data-* custom attributes.
Before inadvertently adopting any non-recommended practices, I would like to clarify the following...
In the list below we have 4 elements:
Element i is a standard element with a data-* attribute
Element ii is a standard element with a custom attribute
Element iii is a custom element with a data-* attribute
Element iv is a custom element with a custom attribute
const toggleDataAttribute = (e) => {
e.target.dataset.inverted = (e.target.dataset.inverted === 'true') ? 'false' : 'true';
}
const toggleCustomAttribute = (e) => {
if (e.target.getAttribute('inverted') === 'true') {
e.target.setAttribute('inverted', 'false');
}
else {
e.target.setAttribute('inverted', 'true');
}
}
const toggleInvert = (e) => {
if (e.target.dataset.inverted) {
toggleDataAttribute(e);
}
else {
toggleCustomAttribute(e);
}
}
// Attach click event TO <div> elements
let divs = [...document.getElementsByTagName('div')];
divs.forEach((div) => div.addEventListener('click', toggleInvert, false));
// Attach click event TO <my-circle> elements
let myCircles = [...document.getElementsByTagName('my-circle')];
myCircles.forEach((myCircle) => myCircle.addEventListener('click', toggleInvert, false));
// Define <my-circle> element
class myCircle extends HTMLElement {
constructor() {
super();
this.root = this.attachShadow({mode: "open"});
}
connectedCallback() {
this.root.appendChild(document.createElement('slot'));
}
}
customElements.define('my-circle', myCircle);
aside {
position: absolute;
top: 0;
right: 0;
width: 280px;
line-height: 24px;
}
div {
float: left;
margin: 0 12px 12px 0;
width: 80px;
height: 80px;
line-height: 80px;
text-align: center;
font-size: 36px;
border-radius: 50%;
cursor: pointer;
}
my-circle {
display: block;
float: left;
margin: 0 12px 12px 0;
width: 80px;
height: 80px;
line-height: 80px;
text-align: center;
font-size: 36px;
background: radial-gradient(#fff, #000);
border-radius: 50%;
cursor: pointer;
}
my-circle:first-of-type {
clear: left;
}
div:nth-of-type(1) {
background: radial-gradient(rgb(255, 255, 0), rgb(255, 0, 0));
}
div:nth-of-type(2) {
background: radial-gradient(rgb(255, 255, 0), rgb(0, 163, 0));
}
my-circle:nth-of-type(1) {
background: radial-gradient(rgb(255, 255, 0), rgb(223, 163, 0));
}
my-circle:nth-of-type(2) {
background: radial-gradient(rgb(255, 127, 127), rgb(255, 0, 0));
}
div[data-inverted="true"],
div[inverted="true"],
my-circle[data-inverted="true"],
my-circle[inverted="true"] {
filter: hue-rotate(180deg);
}
<div data-inverted="false">i</div>
<div inverted="false">ii</div>
<my-circle data-inverted="false">iii</my-circle>
<my-circle inverted="false">iv</my-circle>
<aside>
<p><strong>Click</strong> on each of the circles on the left to invert their backgrounds.</p>
</aside>
Although the set up above works technically, which of the following is true:
A) Custom attributes may be used universally, in standard elements and custom elements.
Conclusion: Elements i, ii, iii & iv are all valid
B) Custom attributes may only be used in custom elements. They are invalid elsewhere.
Conclusion: Elements i, iii & iv are valid, while ii is invalid
C) Data-* attributes are for standard elements, custom attributes are for custom elements.
Conclusion: Elements i & iv are valid, while ii & iii are invalid
D) Custom attributes are not even a thing. Where did you get this idea from?
Conclusion: Elements i & iii are valid, while ii & iv are invalid
Added:
To illustrate my question above, I'd like to give an example of where custom attributes appear not to be valid:
Go to: https://validator.w3.org/nu/#textarea
Select text input
Enter:
<!DOCTYPE html>
<html lang="">
<head>
<title>Test</title>
</head>
<body>
<div data-inverted="false">i</div>
<div inverted="false">ii</div>
</body>
</html>
Check the markup
The validator returns the error:
Error: Attribute inverted not allowed on element div at this point.
From line 10, column 1; to line 10, column 22
i</div>↩↩<div inverted="false">ii</di
Though... I'm not sure if the tool at https://validator.w3.org/nu/ is outdated and / or abandoned and the Error returned should no longer be regarded as an error in 2020 (?)
All 4 usages work, so why should they be invalid?
data- prefix gives the added bonus they are available in element.dataset.
-- Attributes are Attributes -- , nothing special in the Custom Elements API,
apart from observedAttributes(). Yes, you can use data-* attributes there to.
note
class myCircle extends HTMLElement {
constructor() {
super();
this.root = this.attachShadow({mode: "open"});
}
connectedCallback() {
this.root.appendChild(document.createElement('slot'));
}
}
can be written as:
class myCircle extends HTMLElement {
constructor() {
super()
.attachShadow({mode: "open"})
.append(document.createElement('slot'));
}
}
because super() returns 'this'
and attachShadow both sets and returns this.shadowRoot for free
you are not doing anything with appendChild() return value, so append() (which can take multiple parameters) is enough.
Also note there is a toggleAttribute method.
https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/append
https://developer.mozilla.org/en-US/docs/Web/API/Element/toggleAttribute

How to dynamically apply CSS in vue.js?

This is my situation:
I have a web app that allow users to change the UI size. (i.e. small, medium or large button)
Users can change how it looks like dynamically during runtime. All text and form input boxes will be resized.
My project is in Vue.js.
What is the best way to solve this? Loading different css when user click?
Load different CSS while user click the button similar to this . Codepen : https://codepen.io/anon/pen/NJEoVM
HTML
<div id="app" :class="size">
<div class="text">Text</div>
<input class="ipt"/><br/><br/>
<button class="btn" #click="change('small')">Small</button>
<button class="btn" #click="change('medium')">Medium</button>
<button class="btn" #click="change('large')">Large</button>
</div>
CSS
.small .ipt{
width: 100px;
height:30px;
}
.small .text{
font-size: 18px;
}
.medium .ipt{
width: 300px;
height:50px;
}
.medium .text{
font-size: 32px;
}
.large .ipt{
width: 600px;
height:100px;
}
.large .text{
font-size: 64px;
}
Javascript
new Vue({
el: '#app',
data:()=>({
size:'small'
}),
methods:{
change(val){
this.size = val
}
}
});
Actually, you can make use of Custom Properties aka CSS variables.
Firstly, define the button CSS styles
/* button.css */
#buttonRef {
--fontSize: 16px;
font-size: var(--fontSize)
}
The overall flow would be something like the following one e.g
methods: {
changeButtonSize: function(size, event) {
event.preventDefault();
/* size might be either of 's', 'm', 'l' */
/* let the button ref is stored in refs */
const buttonRef = this.$refs[“buttonRef”];
let fontSize = 16;
switch(size) {
case 's':
fontSize = 12;
case 'm':
fontSize = 18;
case 'l':
fontSize = 22;
}
/* dynamically change the value for custom property */
buttonRef.style.setProperty("--fontSize", fontSize);
}
}
you can set up 3 classes:
.small {font-size:12px}
.medium {font-size:18px}
.large {font-size:24px}
Then add or remove them to your main parent div onClick.
If you have discreetly set the font-size on the elements, you'll have to target those elements as such:
.small .description { font-size:12px }
.small .title{ font-size:16px }

Make Different Fonts Display at the Same Actual Size?

When I use two different fonts in a web page at the same font-size, they often display at different actual sizes:
This example uses two Google Fonts, Gentium and Metamorphous at the same font-size, specified as 20px.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<link id="Gentium Book Basic" rel="stylesheet" type="text/css"
href="http://fonts.googleapis.com/css?family=Gentium Book Basic"
media="all">
<link id="Metamorphous" rel="stylesheet" type="text/css"
href="http://fonts.googleapis.com/css?family=Metamorphous" media="all">
</head>
<body style="font-size: 20px">
<span style="font-family: Gentium Book Basic">Test Text Length (Gentium)</span>
<br>
<span style="font-family: Metamorphous">Test Text Length (Metamorphous) </span>
</body>
</html>
A JSBin for this example can be found here.
My understanding of specifying font-size in an absolute length like px was that the font would be scaled to match that length. My expectation is that two different fonts at the same font-size would have either matching height or matching length (I understand the aspect ratios of the fonts may be different). But it doesn't appear that either is the case here. Is there some way I can make two arbitrary fonts display at either the same height or the same length without manually calculating and applying a correction?
EDIT: An example showing the descender to ascender distance for two fonts displayed at the same font size.
Clearly the two distances are not the same for these two fonts as displayed.
EDIT: An example showing letters with and without accents in the two fonts:
Again, clearly the letters are different sizes.
EDIT: Going on what is described in this article, the issue is that font-size controls the displayed size of the em value of the font. But the em value is arbitrary (it doesn't have to correspond to anything within the font, and in particular is not necessarily the height of a lower case 'm'), and does not include the ascenders and descenders, which can be any size at all (example taken from above article):
so the result is that a "100px" font can be just about any effective size whatsoever. The author of the above article computed the range of effective sizes for the Google Web Fonts at the time to be 0.618 to 3.378.
Since the font metrics (such as the em size, the capitals height, the ascender and descender values) are not exposed in CSS, there doesn't seem to be any way within CSS to make two arbitrary fonts the same effective size. For any particular font, you can use a font editor to find the font metric values and use those numbers to scale the font as required. For an arbitrary font, an option is to display some text and use the measured bounding box to determine the effective size and calculate an appropriate scaling factor.
My thanks to everyone who contributed to explaining this!
I spent a lot of time crawling through StackOverflow looking for answers for a similar situation and ended up not finding anything perfect. What I ended up doing is measuring the two fonts, and then adjusting the top margin and scale of the second font to match the first. (By using scale instead of changing the font size, it allows us to not need to re-calculate the text metrics after resizing)
I put together a couple of pens for posterity. Here's the second one, which handles the normalization of font sizes and alignments between two fonts: https://codepen.io/zacholas/pen/oNBPWga
And the first one, that only handles the measuring, is: https://codepen.io/zacholas/pen/ExZwJjx
I guess I have to paste some code in order to link to codepen, so here's all the comparison code:
HTML:
<h1>Welcome</h1>
<p>
<strong>What's all this about?</strong><br>
I've been working on the new version of the Mason image editor app, and in it, I need to compare multiple fonts to replace them with each other to have the final layout not look crappy due to spacing differences between fonts. (To allow users to customize fonts in templates and have them still look nice)
</p>
<p>The first pen focused on getting all the measurements and ratios necessary.</p>
<p>This pen encompasses the second part, which is comparison and normalization of different fonts against a default.</p>
<p>
<strong>How it works</strong><br>
First we get the metrics for the two fonts and compare their top/bottom spacing. We then normalize them to align in a purdy vertically-centered way. And then we scale down the second font's container so that it matches the size of the first.
</p>
<p>Enjoy!</p>
<p><em>- Zach</em></p>
<hr>
<h3>Demo</h3>
<p>If all of my code is working correctly, the text in the "new font adjusted" box should look all purdy and be vertically and horizontally centered.</p>
<p><strong>NOTE:</strong><em> You'll need to make a change in the dropdown before the text in the "new font adjusted" box actually gets adjusted.</em></p>
<label for="font-picker">Choose a font to swap:</label>
<select id="font-picker" disabled>
<option value=""> — Template Default — </option>
</select>
<div >
<div id="image-box" class="flex-row">
<div>
<h6>Original:</h6>
<div class="reference-box">
<div class="text-background">
<div class="text-container text-utc-lander" id="original-text">
Hxy
</div>
</div>
</div>
</div>
<div>
<h6>New font unadjusted:</h6>
<div class="reference-box">
<div class="text-background">
<div class="text-container text-utc-lander" id="unadjusted-text">
Hxy
</div>
</div>
</div>
</div>
<div>
<h6>New font adjusted:</h6>
<div id="modified" class="reference-box">
<div class="text-background">
<div class="scaler" id="adjusted-text-scaler">
<div class="text-container text-utc-lander" id="adjusted-text">
Hxy
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<hr>
<h2>Canvases used for calculating</h2>
<div id="sample-output-container">
</div>
SCSS:
#import 'https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css';
// The font size in the demo boxes
$test-font-size: 200px!default;
// $test-font-size: 100px;
body {
background: #eee;
padding: 10px;
font-family: Arial;
}
hr {
margin: 40px 0;
}
h6 {
font-size: 17px;
margin: 12px 0 5px 0;
}
//* In production, you should probably use code like this to position canvases off-screen:
// .test-canvas {
// position: fixed;
// top: -99999px;
// left: -99999px;
// display:none;
// }
.text-utc-lander { font-family: 'UTCLander-Regular'; }
.text-ar-bonnie { font-family: 'ARBONNIE'; }
.text-adam-cg { font-family: 'ADAMCGPRO'; }
.text-abolition { font-family: 'Abolition-Regular'; }
.text-avenir { font-family: 'AvenirNextLTPro-BoldItalic'; }
.text-agency { font-family: 'AgencyFB-Reg'; }
/* Testing a real life example with absolute CSS done to make a F-ed up font
like UTC lander look good, which we'll then need to modify positioning and
sizing for in order for it to look good with normal fonts */
.flex-row {
display: flex;
justify-content: space-between;
}
#image-box {
.reference-box {
background: url('https://mason-app-staging.herokuapp.com/images/sports_stadium_generic.jpg');
background-size: cover;
position: relative;
width: $test-font-size * 2;
height: $test-font-size * 1.2;
&:before, &:after {
content: '';
left: $test-font-size * .1;
right: $test-font-size * .1;
position: absolute;
height: 1px;
background: rgba(0,0,0,0.1);
z-index: 5;
}
&:before {
top: $test-font-size * 0.245;
}
&:after {
bottom: $test-font-size * 0.245;
}
.text-background {
position: absolute;
left: ($test-font-size * 0.1);
top: ($test-font-size * 0.1);
width: ($test-font-size * 1.8);
height: ($test-font-size * 1);
background:#39b510;
color: #fff;
text-transform: uppercase;
display: flex;
align-items: center;
justify-content: center;
}
.text-container {
margin-top: -10px; // Will be overwritten anyway
text-align: center;
font-size: $test-font-size;
line-height: 1;
}
}
}
#comparison-output {
background: #fff;
padding: 20px;
margin-top: 40px;
flex: 1;
}
//* Debug output from the first example
#sample-output-container {
// * {
// line-height: 1;
// }
> div {
width: 700px;
background: #CCC;
margin-bottom: 20px;
position: relative;
height: 200px;
> .text-container {
background: #fff;
position: absolute;
display: flex;
height: 150px;
left: 25px;
width: 300px;
top: 25px;
align-items: center;
justify-content: center;
> span {
background: #edc79e;
}
}
> .info-box {
font-size: 12px;
font-family: Arial;
background: #fff;
position: absolute;
width: 300px;
top: 25px;
right: 25px;
padding: 10px;
}
}
}
/*
Webfonts
- Code from here down is just base 64'd webfonts.
- All are in "normal" font weight
- Families available:
- 'ARBONNIE';
- 'ADAMCGPRO';
- 'Abolition-Regular';
- 'AgencyFB-Reg';
- 'AvenirNextLTPro-BoldItalic';
- 'UTCLander-Regular';
*/
/* ***** SKIPPING BASE-64'D FONTS FOR STACKOVERFLOW */
JS:
import FontFaceObserver from "https://cdn.skypack.dev/fontfaceobserver#2.1.0";
// var FontFaceObserver = require('fontfaceobserver');
const TYPE_DEFAULT_FONT = 'defaultFont';
const TYPE_CURRENT_FONT = 'currentFont';
// debug output canvases
const removeCalculationCanvases = false;
const allAvailableFonts = [
{ label: 'AR Bonnie', value: 'ARBONNIE' },
{ label: 'Adam CG Pro', value: 'ADAMCGPRO' },
{ label: 'Abolition Regular', value: 'Abolition-Regular' },
{ label: 'Avenir Next LT Pro Bold Italic', value: 'AvenirNextLTPro-BoldItalic' },
{ label: 'Agency FB', value: 'AgencyFB-Reg' },
{ label: 'UTC Lander', value: 'UTCLander-Regular' },
]
const INITIAL_STATE = {
[TYPE_DEFAULT_FONT]: {
label: null,
fontFamily: null,
fontSize: null,
metrics: {},
},
[TYPE_CURRENT_FONT]: {
label: null,
fontFamily: null,
fontSize: null,
metrics: {},
postAdjustmentMetrics: {}
}
}
let state = {
...INITIAL_STATE
}
const _roundToTwo = num => {
return +(Math.round(Number(num) + "e+2") + "e-2");
}
const _roundToFive = num => {
return +(Math.round(Number(num) + "e+5") + "e-5");
}
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const getTextMetrics = async(fontFamily, fontSize, testtext = 'Sixty Handgloves ABC') => {
//* For now we'll just keep the test text hard-coded but maybe we'll pass in the element value at some point. (However, being that the text will be editable I don't think that's wise)
testtext = 'Hxy';
const fontSizePx = fontSize.split('px')[0];
//* Generate a hash from the font name for the canvas ID
const canvasId = Math.abs(fontFamily.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0));
console.log('waiting for font to load')
var font = new FontFaceObserver(fontFamily);
await font.load();
console.log('font loaded');
//* Initialize the test canvas so that we can measure stuff
const testCanvasWidth = 400;
const testCanvasHeight = 200;
const testCanvasPadding = 10;
// const canvasDrawingTextFontSize = 1000;
const canvasDrawingTextFontSize = fontSizePx;
const testCanvas = document.createElement('canvas');
testCanvas.id = (`cvs-${canvasId}-${Math.random().toString(36).substring(7)}`);
testCanvas.className = `test-canvas ${canvasId}`;
testCanvas.width = testCanvasWidth;
testCanvas.height = testCanvasHeight;
// document.body.appendChild(testCanvas);
var testCanvasCtx = testCanvas.getContext("2d");
testCanvas.style.font = `${canvasDrawingTextFontSize}px ${fontFamily}`;
testCanvasCtx.font = [`${canvasDrawingTextFontSize}px`, fontFamily].join(' ');
testCanvasCtx.clearRect(0, 0, testCanvasWidth, testCanvasHeight);
testCanvasCtx.fontFamily = fontFamily;
testCanvasCtx.fillStyle = "#fff";
testCanvasCtx.fillRect(0,0,testCanvas.width, testCanvas.height);
testCanvasCtx.fillStyle = "#333333";
testCanvasCtx.fillText(testtext, testCanvasPadding, testCanvasHeight);
// console.log('before timeout');
// await timeout(3000);
// console.log('timeout done');
document.body.appendChild(testCanvas);
//* Get Core Measurements
var xHeight = testCanvasCtx.measureText("x").height;
var capHeight = testCanvasCtx.measureText("H").height;
// var measuredTextMetrics = testCanvasCtx.measureText("Hxy");
var measuredTextMetrics = testCanvasCtx.measureText(testtext);
//* Make the measurements usable (cast to numbers to allow for nulls)
let metrics = {};
metrics.measured = {
actualBoundingBoxAscent: _roundToFive(measuredTextMetrics.actualBoundingBoxAscent),
actualBoundingBoxDescent: _roundToFive(measuredTextMetrics.actualBoundingBoxDescent),
actualBoundingBoxLeft: _roundToFive(measuredTextMetrics.actualBoundingBoxLeft),
actualBoundingBoxRight: _roundToFive(measuredTextMetrics.actualBoundingBoxRight),
fontBoundingBoxAscent: _roundToFive(measuredTextMetrics.fontBoundingBoxAscent),
fontBoundingBoxDescent: _roundToFive(measuredTextMetrics.fontBoundingBoxDescent),
width: _roundToFive(measuredTextMetrics.width)
};
const fontSizeMultiplicand = fontSizePx / canvasDrawingTextFontSize;
const {
actualBoundingBoxAscent,
// actualBoundingBoxDescent,
// actualBoundingBoxLeft,
// actualBoundingBoxRight,
fontBoundingBoxAscent,
fontBoundingBoxDescent,
} = metrics.measured;
metrics.calculated = {
gapAboveText: _roundToFive((fontBoundingBoxAscent - actualBoundingBoxAscent) * fontSizeMultiplicand),
gapBelowText: _roundToFive(fontBoundingBoxDescent * fontSizeMultiplicand),
textHeight: _roundToFive(actualBoundingBoxAscent * fontSizeMultiplicand),
totalHeight: _roundToFive((fontBoundingBoxAscent + fontBoundingBoxDescent) * fontSizeMultiplicand),
};
const {
gapBelowText, gapAboveText, textHeight, totalHeight
} = metrics.calculated;
metrics.calculated.gapBelowTextPercent = _roundToFive(gapBelowText / totalHeight);
metrics.calculated.gapAboveTextPercent = _roundToFive(gapAboveText / totalHeight);
metrics.calculated.gapTopBottomRatio = _roundToFive(gapAboveText / gapBelowText);
metrics.calculated.textHeightPercent = _roundToFive(textHeight / totalHeight);
metrics.calculated.baselineMarginTop = gapBelowText - gapAboveText;
if(removeCalculationCanvases === true){
testCanvas.remove(); // cleanup
}
return metrics;
};
const setFontState = async(fontFamily, fontSize, fontLabel, type = TYPE_CURRENT_FONT) => {
if(fontFamily){
console.log('about to get text metrics')
const metrics = await getTextMetrics(fontFamily, fontSize);
console.log('metrics received');
state[type] = {
label: fontLabel ? fontLabel : fontFamily,
fontFamily,
fontSize,
metrics
}
}
else {
state[type] = {
...INITIAL_STATE[type]
}
}
return true;
}
const watchForFontChange = async() => {
document.addEventListener('input', async(event) => {
if (event.target.id !== 'font-picker') return; // Only run on the font change menu
let label = null;
if(
event.target.options.length &&
typeof event.target.options[event.target.selectedIndex] !== 'undefined' &&
event.target.options[event.target.selectedIndex].text
) {
label = event.target.options[event.target.selectedIndex].text;
}
// For now just grab font size from the default font state, but probably will change later
const fontFamily = event.target.value;
const fontSize = state[TYPE_DEFAULT_FONT].fontSize;
await setFontState(fontFamily, fontSize, label);
console.log('font changed', state);
//* Set the font families in the display
if(fontFamily){
document.getElementById(`unadjusted-text`).style.fontFamily = fontFamily;
document.getElementById(`adjusted-text`).style.fontFamily = fontFamily;
}
else {
document.getElementById(`unadjusted-text`).style.fontFamily = null;
document.getElementById(`adjusted-text`).style.fontFamily = null;
}
//* Calculate the adjustments for the new font compared to the baseline
// const currentFontSize = parseInt(state.currentFont.fontSize,10);
const defaultFontMetrics = state.defaultFont.metrics;
const currentFontMetrics = state.currentFont.metrics;
// const fontSizeAdjustPx = defaultFontMetrics.calculated.textHeight - currentFontMetrics.calculated.textHeight;
// const fontSizeAdjustPcnt = _roundToFive(fontSizeAdjustPx / currentFontMetrics.calculated.textHeight);
//* Apply the adjustments
// const newFontSize = currentFontSize + (currentFontSize * fontSizeAdjustPcnt);
// console.log('newFontSize', newFontSize);
const textToAdjust = document.getElementById(`adjusted-text`);
// const fontSizeStr = `${newFontSize}px`;
textToAdjust.style.marginTop = `${currentFontMetrics.calculated.baselineMarginTop}px`;
const scaler = document.getElementById('adjusted-text-scaler');
const scale = _roundToTwo(defaultFontMetrics.calculated.textHeight / currentFontMetrics.calculated.textHeight);
scaler.style.transform = `scale(${scale})`;
}, false);
}
const addFontOptionsToDropdown = () => {
const parentSelect = document.getElementById(`font-picker`);
for(let i=0; i < allAvailableFonts.length; i++){
const thisOption = allAvailableFonts[i];
if(thisOption.value){
const label = thisOption.label ? thisOption.label : thisOption.value;
const thisOptionTag = document.createElement("option");
thisOptionTag.value = thisOption.value;
const thisOptionText = document.createTextNode(label);
thisOptionTag.appendChild(thisOptionText);
parentSelect.appendChild(thisOptionTag);
}
}
}
const parseDefaultFont = async() => {
const thisText = document.getElementById(`original-text`);
// We might need to do some special stuff for uppercase vs non-uppercase text
const thisTextStyle = window.getComputedStyle(thisText);
const textTransform = thisTextStyle.getPropertyValue('text-transform');
const marginTop = thisTextStyle.getPropertyValue('margin-top');
console.log('marginTop', marginTop);
const uppercase = textTransform === 'uppercase';
const fontFamily = thisTextStyle.getPropertyValue('font-family');
const fontSize = thisTextStyle.getPropertyValue('font-size');
console.log('fontSize', fontSize);
await setFontState(fontFamily, fontSize, null, TYPE_DEFAULT_FONT);
document.getElementById(`original-text`).style.marginTop = `${state.defaultFont.metrics.calculated.baselineMarginTop}px`;
return !! fontFamily;
}
const init = async() => {
console.log(' ');
console.log(' ');
console.log(' ');
console.log('initialized.');
const defaultFont = await parseDefaultFont();
if(defaultFont){
addFontOptionsToDropdown(); // Parse JSON object into the select html tag
await watchForFontChange();
}
else {
// Handle Error -- for some reason there wasn't a font family for the default text.
}
document.getElementById('font-picker').disabled = false;
console.log('state after init done', state);
}
//* Wait for all the base 64'd fonts to load before we run it
document.addEventListener("DOMContentLoaded", (ready => {
init();
// setTimeout(function(){ init(); }, 1000);
}));
Think of the font-size not as the actual size of the individual characters themselves, but as the size of the blocks that contain each character, just like typeset letters:
The size of the blocks is defined in your CSS (using px, pts, ems, etc) but the actual size of the characters within those blocks can vary depending on the font used.
The actual, physical height of any given portion of the font depends on the user-defined DPI setting, current element font-size, and the particular font being used.
https://en.wikipedia.org/wiki/Em_(typography)#CSS
You can use the font-size-adjust property to help alter one of those fonts to scale it closer to the other: https://developer.mozilla.org/en-US/docs/Web/CSS/font-size-adjust although its support is currently limited to Firefox: http://caniuse.com/#feat=font-size-adjust
Font size is the size of the glyph from the ascender, such as the top of the letter 'h', to the descender, such as the bottom of the letter 'g'. If you set your font size to 20px, the length from the top of the letter 'h' to the bottom of the letter 'g' will be 20px. Some letters have terminals or spurs, the ends of a letter, may extend a px or two higher on some letters.
In your example, there is a px difference between the two fonts. The Metamorphous font has a mark above some letters that Gentium does not have and that is what accounts for the height difference.
You can read more here.
EDIT: See here with the "caron" above the C compared to the two Gentium letters on the right.
you should rather use something like rem then px :) as rem is a relative measure unit and px is absolute. But fonts always have a different size and imo its not possible what you want to achieve.

Is there a way to show / hide a <div> depending on the size of the browser?

Angular JS code I am working on has media queries that can be used to limit the display of blocks with code like this:
#media screen and (max-width: 370px) {
#testGrid {
.gridHeader {
div:nth-child(2),
div:nth-child(3),
div:nth-child(n+7) {
display: none;
}
div:nth-child(6) {
border-top-right-radius: 0.4rem;
}
}
.gridBody {
div {
div:nth-child(2),
div:nth-child(3),
div:nth-child(n+7) {
display: none;
}
}
}
}
}
My comment here was that it's not good to use things like div:nth-child(2) as this would easily break if another column was added. Plus it's also difficult to maintain. I suggested to give the column names class names that matched the contents of the columns.
Still this means that I have the code that defines what shows and what does not show far removed from the HTML. Does anyone have any suggestions on a way that I could do this with AngularJS that would have the showing and hiding of columns next to the actual <div>s
You can get the current width from the $window service so you could try something like this:
DEMO
app.controller('MainCtrl', function($scope, $window) {
$scope.name = 'World';
angular.element($window).bind('resize', function(){
$scope.hideThing = ($window.innerWidth < 400);
// have to manually update $scope as angular won't know about the resize event
$scope.$digest();
});
});
Then in your HTML
<body ng-controller="MainCtrl">
<p ng-hide="hideThing" >Hello {{name}}!</p>
</body>

Margin, position and padding not working when display:inline is set. also weird behaviour from relative position

I have two CSS classes:
.class1 {
height: 100%;
width: 300px;
border: 1px none #B0B0B0;
position: relative;
display: inline;
left: 10px;
}
.class2 {
height: 100%;
width: 200px;
position: relative;
display: inline;
margin-left: 15px;
background-color: #00CCCC;
border-top-width: 1px;
border-right-width: 1px;
border-bottom-width: 1px;
border-left-width: 1px;
border-top-style: solid;
border-right-style: solid;
border-bottom-style: solid;
border-left-style: solid;
}
Now, as you can see, they're both set to display in a line (no line breaks in between elements). Which works correctly. But for some reason, ever since I set the display to inline, the Padding, the Positioning and the Margin CSS have all just stopped working. I can add a margin-left 10inches and nothing will happen. Same with padding and positioning.
Can anyone explain how to fix this?
Also, I have the relative position set on both classes, yet when viewing the page in a browser, .class2 over laps .class1 when its supposed to be just after .class1.
Any ideas?
EDIT:
Okay, so I've done a JSFiddle, but it seems to be playing up even more there....
Looks like the Width is not working....
here it is:
http://jsfiddle.net/zYbwh/1/
You need to use
display: inline-block;
instead. margin doesn't work with display: inline elements, however with inline-block it does. You can then have an inline element with margins and explicit widths/heights.
To make this work in IE7, add these two lines:
*display: inline;
zoom: 1;
It's horrible, but it works.
I know this is quite a late answer but I wrote a jQuery plugin which support padding on inline elements (with word breaking) see this JSfiddle:
http://jsfiddle.net/RxKek/
Plugin Code:
$.fn.outerHTML = function () {
// IE, Chrome & Safari will comply with the non-standard outerHTML, all others (FF) will have a fall-back for cloning
return (!this.length) ? this : (this[0].outerHTML || (
function (el) {
var div = document.createElement('div');
div.appendChild(el.cloneNode(true));
var contents = div.innerHTML;
div = null;
return contents;
})(this[0]));
};
/*
Requirements:
1. The container must NOT have a width!
2. The element needs to be formatted like this:
<div>text</div>
in stead of this:
<div>
text
</div>
*/
$.fn.fixInlineText = function (opt) {
return this.each(function () {
//First get the container width
var maxWidth = opt.width;
//Then get the width of the inline element
//To calculate the correct width the element needs to
//be 100% visible that's why we make it absolute first.
//We also do this to the container.
$(this).css("position", "absolute");
$(this).parent().css("position", "absolute").css("width", "200%");
var width = $(this).width();
$(this).css("position", "");
$(this).parent().css("position", "").css("width", "");
//Don't do anything if it fits
if (width < maxWidth) {
return;
}
//Check how many times the container fits within the box
var times = Math.ceil(width / maxWidth);
//Function for cleaning chunks
var cleanChunk = function (chunk) {
var thisChunkLength = chunk.length - 1;
if (chunk[0] == " ") chunk = chunk.substring(1);
if (chunk[thisChunkLength] == " ") chunk = chunk.substring(0, thisChunkLength);
return chunk;
};
//Divide the text into chunks
var text = $(this).html();
var textArr = text.split(" ");
var chunkLength = Math.ceil((textArr.length - 1) / times);
var chunks = [];
var curChunk = "";
var curChunkCount = 0;
var isParsingHtml = false;
//Loop through the text array and split it into chunks
for (var i in textArr) {
//When we are parsing HTML we don't want to count the
//spaces since the user doesn't see it.
if (isParsingHtml) {
//Check for a HTML end tag
if (/<\/[a-zA-Z]*>/.test(textArr[i]) || /[a-zA-Z]*>/.test(textArr[i])) {
isParsingHtml = false;
}
} else {
//Check for a HTML begin tag
if (/<[a-zA-Z]*/.test(textArr[i])) {
isParsingHtml = true;
}
}
//Calculate chunks
if (curChunkCount == (chunkLength - 1) && !isParsingHtml) {
curChunk += textArr[i] + " ";
chunks.push(cleanChunk(curChunk));
curChunk = "";
curChunkCount = -1;
} else if ((i == (textArr.length - 1))) {
curChunk += textArr[i];
chunks.push(cleanChunk(curChunk));
break;
} else {
curChunk += textArr[i] + " ";
}
if (!isParsingHtml) {
curChunkCount++;
}
}
//Convert chunks to new elements
var el = $($(this).html("").outerHTML());
for (var x in chunks) {
var new_el = el.clone().html(chunks[x]).addClass("text-render-el");
var new_el_container = $("<div/>").addClass("text-render-container");
new_el_container.append(new_el);
$(this).before(new_el_container);
}
//Finally remove the current element
$(this).remove();
});
};
Thats the problem you get when using templates, ive programmed a site in php, but the design is killing me.
So i try'd some rocket fuel for webdesigners.
And this is the problems i keep getting every step of the way...
Inline-block does not work for me, nothing works, becouse it is not my design and i dont know the setup.
Ive tryd doing the design myself, but i am out of time, i need a design yesterday.
I suggest you take what u need from the templates and delete everything else, that will schrink your problem, and save you time.

Resources