Div height based on rendered character size - html

Essentially I'm wondering whether there's any mechanism to map your font's characters to a div's dimensions, taller letters creating taller divs, etc
There's units like em, ex, and ch that are supposed to represent font size but these are the font as a whole, not relative to the present characters
This may just not be possible within css/html
I've created this code example of what I mean, red being the div bounds, blue being my desired output
body {
margin : 5px 0 0 5px;
padding : 0;
display : inline-flex;
flex-flow : column;
}
.set {
display : inline-flex;
flex-flow : row;
padding : 0;
}
.overlay {
position : absolute;
margin : 5px 0px 5px 5px;
width : 60px;
box-shadow : 0 0 0 2px blue;
}
.text {
margin : 5px 5px 5px 5px;
font-family : sans-serif;
font-size : 32px;
line-height : 32px;
box-shadow : 0 0 0 2px red;
width : 60px;
text-align : center;
}
.overlay.overlay-1 {
margin-top : -26px;
height : 13px;
}
.overlay.overlay-2 {
margin-top : -34px;
height : 22px;
}
.overlay.overlay-3 {
margin-top : -28px;
height : 23px;
}
<html>
<body>
<div class="set">
<div>
<div class="text">xxx</div>
</div>
<div>
<div class="text">xxx</div>
<div class="overlay overlay-1"></div>
</div>
</div>
<div class="set">
<div>
<div class="text">Xxx</div>
</div>
<div>
<div class="text">Xxx</div>
<div class="overlay overlay-2"></div>
</div>
</div>
<div class="set">
<div>
<div class="text">xxy</div>
</div>
<div>
<div class="text">xxy</div>
<div class="overlay overlay-3"></div>
</div>
</div>
</body>
</html>

It seems like there is no programatic way of achieving this with CSS, as the relative heights in CSS refer to the font dimensions, not individual characters.
Edit: found a Javascript code that converts a line of text into an image, then measures the image size in pixels. Maybe you can use that.
Check this post
function measureTextHeight(fontSizeFace) {
// create a temp canvas
var width = 1000;
var height = 60;
var canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext("2d");
// Draw the entire a-z/A-Z alphabet in the canvas
var text = "o";
ctx.save();
ctx.font = fontSizeFace;
ctx.clearRect(0, 0, width, height);
ctx.fillText(text, 0, 40);
ctx.restore();
// Get the pixel data from the canvas
var data = ctx.getImageData(0, 0, width, height).data,
first = false,
last = false,
r = height,
c = 0;
// Find the last line with a non-transparent pixel
while (!last && r) {
r--;
for (c = 0; c < width; c++) {
if (data[r * width * 4 + c * 4 + 3]) {
last = r;
break;
}
}
}
// Find the first line with a non-transparent pixel
while (r) {
r--;
for (c = 0; c < width; c++) {
if (data[r * width * 4 + c * 4 + 3]) {
first = r;
break;
}
}
// If we've got it then return the height
if (first != r) return last - first;
}
// error condition if we get here
return 0;
}

Related

Display stars rating around rounded profile image [CSS]

Hi guys i'm creating rating system and i want to display the stars elements around profile image. I have profile image set to border-radius 50% which makes it rounded, but i'm struggle to set the div with the rates on the bottom of the profile image, what i'm looking for is something like this:
Example
What i have by now it's this:
body{
box-sizing: border-box;
margin: 0 auto;
background-color: #fff;
}
.avatar-inner{
width: 100px;
height: 100px;
}
.avatar{
width: 100%;
border-radius: 50%;
}
.rate-holder{
font-size: 25px;
color: #fff;
background-color: #000;
}
<div class="avatar-holder">
<div class="avatar-inner">
<img class="avatar" src="https://i.ibb.co/bmfXcFw/avatar.png" alt="" srcset="">
<div class="rate-holder">
<span>*</span>
</div>
</div>
</div>
[1]
As #RenevanderLende mentioned in comments for doing that you can use
transform:rotate() translateX();
Here's an example:
and I should mention that in provided example you have to set icon
sizes and their margins manually to match your photo-container
dimensions
// icons count
const count = 5;
// icons size in px
const size = 18;
// icons margin in px
const marign = 4;
// icons position in deg
// e.g. if you wanna position all icons at the bottom side use: 55
// e.g. if you wanna position all icons at the left side use: 120
// e.g. if you wanna position all icons at the top side use: 240
// e.g. if you wanna position all icons at the right side use: 330
const position = 0;
// get list of all photo containers
const photoContainer = document.querySelectorAll(".photo-container");
// loop through each container and generate rating stars
[...photoContainer].forEach((container) => {
// gets radius of the container
const r = container.offsetHeight / 2;
const offset = (size / Math.PI) * -1;
const ratingContainer = document.createElement("div");
ratingContainer.className = "rating-container";
const iconsHolder = document.createElement("div");
iconsHolder.className = "icons-holder";
const pos = [];
for (let i = 0; i < count; i++) {
// creates the angles array
pos.push(i * (size + offset + marign));
// creates some holders in a circularly positioned way, to hold the rating stars
const div = document.createElement("div");
div.style.position = "absolute";
div.style.display = "block";
div.style.height = `${size}px`;
div.style.width = `${size}px`;
// sets the icons positions circularly
div.style.transform = `rotate(${pos[i]}deg) translateX(${r - size + Math.abs(offset)}px)`;
// creates the star icons
const icon = document.createElement("span");
icon.className = "icon fa fa-star";
icon.style.fontSize = `${size}px`;
// arranges icons vertically(90deg)
// remove this part to see what happens
icon.style.transform = `rotate(${pos[i] * -1}deg)`;
div.append(icon);
iconsHolder.append(div);
}
// changes the all icons position
iconsHolder.style.transform = `rotate(${position}deg)`;
ratingContainer.append(iconsHolder);
container.append(ratingContainer);
});
.photo-container {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 200px;
height: 200px;
border-radius: 50%;
overflow: hidden;
}
.rating-container {
position: absolute;
inset: 0;
}
.rating-container .icons-holder {
position: relative;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
.icons-holder>div {
cursor: pointer;
}
.icons-holder>div .icon {
color: orange;
}
.icons-holder>div .icon:hover {
color: darkgoldenrod;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<div class="photo-container">
<img style="width: 100%;" src="https://i.ibb.co/bmfXcFw/avatar.png" />
</div>
<div class="photo-container">
<img style="width: 100%;" src="https://i.ibb.co/bmfXcFw/avatar.png" />
</div>
Update:
[2]
You can also do it without using css transform (explanations are provided as comments in code.)
Example 1:
function ratingStars(root, options) {
// returns width and height of an element
const getRadius = (element) => {
return Math.round(element.offsetWidth / 2);
};
// takes radius, degrees returns x,y
const pos = (r, theta) => {
return {
// theta * (Math.PI / 180) converts radians to degrees
x: Math.round(r * Math.cos(theta * (Math.PI / 180))),
y: Math.round(r * Math.sin(theta * (Math.PI / 180))),
};
};
// default options
let op = {
count: 6,
icons_class: "rating-icons",
rotate: 0, // deg
clock_wise: true, // boolean
size: 28, // pt
icons_margin: 3.8, // px
edge_margin: 0, // px
};
// merges custom options with defaults
op = { ...op,
...options
};
// gets radius of the root element
const radius = getRadius(root); //pixels
if (radius) {
// creates the main holder of all icons
const rating = document.createElement("div");
rating.style.position = "absolute";
// moves the holder to the center of the root element
rating.style.left = `${radius - op.size / 2}px`;
rating.style.top = `${radius - op.size / 2}px`;
root.append(rating);
// generates the required degrees based on count and pushes to the degs
let degs = [];
// runs if the clock_wise is set on true
if (op.clock_wise == true)
for (let i = 0; i < op.count; i++) degs.push(Math.round(i * (op.size / Math.PI + op.icons_margin)));
// runs if the clock_wise is set on false
else
for (let i = 0; i < op.count; i++) degs.push(-Math.round(i * (op.size / Math.PI + op.icons_margin)));
// loops through the degrees and create a holder for each icon
degs.forEach((deg) => {
// creates a holder for each icon
let icon = document.createElement("div");
icon.style.position = "absolute";
icon.style.textAlign = "center";
// positions the holders in a circular way using pos() function that we created first
icon.style.left = `${pos(radius - op.size / 2 - op.edge_margin, deg + op.rotate).x}px`;
icon.style.top = `${pos(radius - op.size / 2 - op.edge_margin, deg + op.rotate).y}px`;
// appends the star icons the the holder
icon.innerHTML = `<span style="font-size: ${op.size * 0.75}pt;" class="${op.icons_class} fa fa-star"></span>`;
rating.append(icon);
});
}
}
const avatar = document.querySelector(".avatar");
ratingStars(avatar);
.avatar {
position: relative;
width: 300px;
height: 300px;
border-radius: 50%;
overflow: hidden;
background: red;
}
.rating-icons {
color: orange;
cursor: pointer;
transition: all 0.5s ease;
}
.rating-icons:hover {
color: yellow;
transform: scale(1.15) rotate(70deg);
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<div class="avatar">
<img src="https://i.pravatar.cc/300?img=11" style="height: 100%" />
</div>
Example 2:
(includes an example for each option)
function ratingStars(root, options) {
// returns width and height of an element
const getRadius = (element) => {
if (element.offsetHeight == element.offsetWidth) return Math.round(element.offsetWidth / 2);
else console.error("the given element isn't a circle");
};
// takes radius, degrees returns x,y
const pos = (r, theta) => {
return {
// theta * (Math.PI / 180) converts radians to degrees
x: Math.round(r * Math.cos(theta * (Math.PI / 180))),
y: Math.round(r * Math.sin(theta * (Math.PI / 180))),
};
};
// default options
let op = {
count: 6,
icons_class: "rating-icons",
rotate: 0, // deg
clock_wise: true, // boolean
size: 20, // pt
icons_margin: 8, // px
edge_margin: 0, // px
};
// merges custom options with defaults
op = { ...op,
...options
};
// gets radius of the root element
const radius = getRadius(root); //pixels
if (radius) {
// creates the main holder of all icons
const rating = document.createElement("div");
rating.style.position = "absolute";
// moves the holder to the center of the root element
rating.style.left = `${radius - op.size / 2}px`;
rating.style.top = `${radius - op.size / 2}px`;
root.append(rating);
// generates the required degrees based on count and pushes to the degs
let degs = [];
// runs if the clock_wise is set on true
if (op.clock_wise == true)
for (let i = 0; i < op.count; i++) degs.push(Math.round(i * (op.size / Math.PI + op.icons_margin)));
// runs if the clock_wise is set on false
else
for (let i = 0; i < op.count; i++) degs.push(-Math.round(i * (op.size / Math.PI + op.icons_margin)));
// loops through the degrees and create a holder for each icon
degs.forEach((deg) => {
// creates a holder for each icon
let icon = document.createElement("div");
icon.style.position = "absolute";
icon.style.textAlign = "center";
// positions the holders in a circular way using pos() function that we created first
icon.style.left = `${pos(radius - op.size / 2 - op.edge_margin, deg + op.rotate).x}px`;
icon.style.top = `${pos(radius - op.size / 2 - op.edge_margin, deg + op.rotate).y}px`;
// appends the star icons the the holder
icon.innerHTML = `<span style="font-size: ${op.size * 0.75}pt;" class="${op.icons_class} fa fa-star"></span>`;
rating.append(icon);
});
}
}
const avatars = document.querySelectorAll(".avatar");
ratingStars(avatars[0]);
ratingStars(avatars[1], {
icons_class: 'rating-icons red'
});
ratingStars(avatars[2], {
rotate: 143
});
ratingStars(avatars[3], {
clock_wise: false
});
ratingStars(avatars[4], {
size: 28
});
ratingStars(avatars[5], {
icons_margin: 20
});
ratingStars(avatars[6], {
edge_margin: 20
});
ratingStars(avatars[7], {
count: 4,
icons_class: 'rating-icons green',
rotate: 50,
clock_wise: true,
size: 30,
icons_margin: 40,
edge_margin: 15
});
ratingStars(avatars[8], {
count: 15
});
body {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
margin: 0;
background: radial-gradient(circle farthest-corner at 10% 20%, rgba(90, 92, 106, 1) 0%, rgba(32, 45, 58, 1) 81.3%);
}
.avatar {
position: relative;
width: 200px;
height: 200px;
border-radius: 50%;
overflow: hidden;
background: red;
}
.rating-icons {
color: orange;
cursor: pointer;
transition: all 0.5s ease;
}
.rating-icons:hover {
color: darkgoldenrod;
transform: scale(1.15) rotate(70deg);
}
.red {
color: red;
}
.red:hover {
color: darkred;
}
.green {
color: green;
}
.green:hover {
color: yellowgreen;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<div style="display: flex">
<div class="avatar" style="width: 300px; height: 300px;">
<img src="https://i.pravatar.cc/300?random=1" style="height: 100%" />
</div>
</div>
<div style="display: flex">
<div class="avatar">
<img src="https://i.pravatar.cc/300?random=1" style="height: 100%" />
</div>
<div class="avatar">
<img src="https://i.pravatar.cc/300?random=2" style="height: 100%" />
</div>
<div class="avatar">
<img src="https://i.pravatar.cc/300?random=3" style="height: 100%" />
</div>
<div class="avatar">
<img src="https://i.pravatar.cc/300?random=5" style="height: 100%" />
</div>
</div>
<div style="display: flex">
<div class="avatar">
<img src="https://i.pravatar.cc/300?random=6" style="height: 100%" />
</div>
<div class="avatar">
<img src="https://i.pravatar.cc/300?random=7" style="height: 100%" />
</div>
<div class="avatar">
<img src="https://i.pravatar.cc/300?random=8" style="height: 100%" />
</div>
<div class="avatar">
<img src="https://i.pravatar.cc/300?random=4" style="height: 100%" />
</div>
</div>

HTML 5 Using canvas in flexbox causes weird scaling

When using canvas in a flexbox and drawing rects of the same size it appears to be glitching.
Take a look at the following example:
It draws a one by one pixel rect to your cursor location but the drawn rectangles are of different size relative to the distance of the top left corner.
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
canvas.addEventListener("click", (event) => {
let x = event.offsetX;
let y = event.offsetY;
ctx.fillRect(x, y, x + 1, y + 1);
});
#container {
display: flex;
}
#first {
flex: 1 1 auto;
background-color: green;
width: 60px;
}
#second {
flex: 2 1 auto;
background-color: red;
}
canvas {
object-fit:none;
object-position:top left;
}
#third {
flex: 1 1 auto;
background-color: green;
width: 60px;
}
<div id="container">
<div id="first"></div>
<div id="second">
<canvas id="canvas" height="160px"></canvas>
</div>
<div id="third"></div>
</div>
How do I prevent this from happening?
Check out https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillRect. The third and fourth arguments for fillRect are the height and width, not positions. So if I understand correctly, you would want these both to just be 1 if you are looking for a 1x1 square.

Make sticky element with overflow hidden expand

How can I make a sticky element, with overflow hidden, expand to fill parent element that has scroll on overflow?
A minor example:
.wrap {
position: absolute;
width: 250px;
height: 80px;
overflow: scroll;
background: #844;
}
.stick {
position: sticky;
white-space: nowrap;
overflow: hidden;
background: #484;
top: 0;
}
.text {
color: #ccc;
}
<div class="wrap">
<div class="stick">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 10 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40</div>
<pre class="text">Hello and good bye to the sticky scroll once the wrap area scrolls past the initial width
a
b
c
d
e
f
g
h
</pre>
</div>
Here the green line is sticky, but it does not fill the width of the scroll element, which is what I'm trying to achieve. The effect is visible once one start scrolling. I can solve it by JS, but hoping to do it with CSS.
The sticky element has overflow hidden as it, in real code, contains a wide canvas as a sub element. This element is wider then the content, as in "text" above, and should not be visible beyond the size of "text".
A picture:
The Yellow part is sticky to top: Scroll up / down it stays at top.
The Yellow part scrolls left / right. Follows the blue content left / right.
The Yellow part does not expand the blue part / viewable content AKA overflow hidden.
The Yellow part has full width of blue part
So the yellow sticky should:
Have same width as blue. Anything beyond this hidden
Stay at top of view-port if window scroll up/ down
Follow content left / right
In the real project I use it in a MDI layout with multiple absolute positioned "windows" having one sticky element on top and left of content area. Much like for example when you have a picture open in GIMP. (The pixel bars). Too complex to post code for it, but here is a small mock-up:
The "window" can be resized by dragging element in bottom-right corner.
(function() {
"use strict";
const spacer = {
el: null,
sz: {
small: [100, 100],
wide: [1000, 100],
high: [100, 1000],
big: [1000, 1000]
},
change: function (ev) {
let z = spacer.sz[ev.target.value];
spacer.el.style.width = z[0] + 'px';
spacer.el.style.height = z[1] + 'px';
},
init: function () {
spacer.el = document.querySelector('.content-spacer');
spacer.el.addEventListener('change', spacer.change);
}
};
const resizer = {
el: null,
overlay: null,
move: function (ev) {
ev.preventDefault();
ev.stopPropagation();
resizer.el_rz.style.marginTop = (ev.layerY - resizer.pos[0]) + 'px';
resizer.el_rz.style.marginLeft = (ev.layerX - resizer.pos[1]) + 'px';
},
end: function () {
window.removeEventListener('mousemove', resizer.move);
window.removeEventListener('mouseup', resizer.end);
resizer.overlay.style.display = 'none';
},
start: function (ev) {
let t = ev.target,
cs = getComputedStyle(t)
;
ev.preventDefault();
ev.stopPropagation();
resizer.el_rz = ev.target;
resizer.pos = [
ev.clientX - (parseInt(cs.marginLeft) || 0),
ev.clientY - (parseInt(cs.marginTop) || 0),
];
window.addEventListener('mouseup', resizer.end);
window.addEventListener('mousemove', resizer.move);
resizer.overlay.style.display = 'block';
},
init: function () {
resizer.el = document.getElementById("sizer");
resizer.overlay = document.getElementById("overlay");
resizer.el.addEventListener('mousedown', resizer.start);
}
};
function fill_numbers(el) {
let i, s = '';
for (i = 1; i < 500; ++i)
s += (i % 10) + ' ';
el.textContent = s;
}
resizer.init();
spacer.init();
fill_numbers(document.querySelector('.top-line-c'));
fill_numbers(document.querySelector('.left-line-c'));
})();
body {
position : relative;
width : 100vw;
height : 100%;
padding : 0;
margin : 0;
}
#overlay {
position : absolute;
display : none;
z-index : 100;
height : 100vh;
width : 100vw;
opacity : 0.1;
background : #333;
z-index : 100;
cursor : move;
}
.window {
position : absolute;
overflow : hidden;
margin-top : 5px;
margin-left : 5px;
min-width : 10px;
min-height : 10px;
background : #988;
padding : 0;
}
.sizer {
position : relative;
background : #8a8;
bottom : 0;
right : 0;
width : 15px;
height : 15px;
margin-top : 120px;
margin-left : 250px;
cursor : move;
}
.wrap {
position : relative;
background : #565;
width : 100%;
height : 100%;
}
.content-outer {
overflow : hidden;
top : 0;
bottom : 0;
right : 0;
left : 0;
position : absolute;
background : #a77;
padding : 0;
}
.corner {
position : sticky;
width : 25px;
height : 25px;
top : 0;
left : 0;
z-index : 150;
background : red;
}
.top-line {
top : 0;
margin-top : -25px;
margin-left : 25px;
position : sticky;
height : 25px;
background : pink;
width : 100%;
white-space : nowrap;
z-index : 100;
overflow : hidden;
}
.left-line {
position : sticky;
max-height : 100%;
overflow : hidden;
background : #494;
width : 25px;
left : 0;
z-index : 100;
text-align : center;
}
.left-line-c {
height : 5000px;
}
.content-inner {
position : absolute;
overflow : scroll;
top : 25px;
right : 0;
bottom : 0;
left : 0;
}
.content-text {
position : absolute;
top : 0;
left : 0;
right : 0;
bottom : 0;
margin : 25px ;
}
.content-spacer {
background: rgb(186,124,13);
background: linear-gradient(90deg, rgba(186,124,13,1) 0%, rgba(112,25,58,0.9037815809917717) 53%, rgba(181,0,255,1) 100%);
margin : 15px;
width : 1000px;
height : 100px;
}
ul {
list-style : none;
margin : 0;
padding : 0;
}
.header {
background : #000;
color : #aaa;
height : 25px;
}
<div id="overlay"></div>
<div class="wrap">
<div class="window">
<div class="content-outer">
<div class="header"><span>Header</span></div>
<div class="content-inner">
<div class="corner">C</div>
<div class="top-line">
<div class="top-line-c"></div>
</div>
<div class="left-line">
<div class="left-line-c"></div>
</div>
<div class="content-text">
<div class="content-spacer">
<ul>
<li><label><input type="radio" name="sp" value="small">small</label></li>
<li><label><input type="radio" name="sp" value="wide" checked>wide</label></li>
<li><label><input type="radio" name="sp" value="high">high</label></li>
<li><label><input type="radio" name="sp" value="big">big</label></li>
</ul>
</div>
</div>
</div>
</div>
<div class="sizer" id="sizer" tabindex="0"></div>
</div>
</div>
See if that is what you're needing :
.wrap {
position: absolute;
width: 250px;
height: 80px;
overflow: scroll;
background: #844;
}
.stick {
position: sticky;
display: table;
white-space: nowrap;
overflow: hidden;
background: #484;
}
.text {
color: #ccc;
}
<div class="wrap">
<div class="stick">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 10 21 22 23 24 25 26 27 28 29 30 31 32 33</div>
<pre class="text">Hello and good bye to the sticky scroll once the wrap area scrolls past the initial width</pre>
</div>
This is going to expand the child <div> to the total content of the parent <div>.
Below the same example with the second snippet attached :
(function() {
"use strict";
const spacer = {
el: null,
sz: {
small: [100, 100],
wide: [1000, 100],
high: [100, 1000],
big: [1000, 1000]
},
change: function (ev) {
let z = spacer.sz[ev.target.value];
spacer.el.style.width = z[0] + 'px';
spacer.el.style.height = z[1] + 'px';
},
init: function () {
spacer.el = document.querySelector('.content-spacer');
spacer.el.addEventListener('change', spacer.change);
}
};
const resizer = {
el: null,
overlay: null,
move: function (ev) {
ev.preventDefault();
ev.stopPropagation();
resizer.el_rz.style.marginTop = (ev.layerY - resizer.pos[0]) + 'px';
resizer.el_rz.style.marginLeft = (ev.layerX - resizer.pos[1]) + 'px';
},
end: function () {
window.removeEventListener('mousemove', resizer.move);
window.removeEventListener('mouseup', resizer.end);
resizer.overlay.style.display = 'none';
},
start: function (ev) {
let t = ev.target,
cs = getComputedStyle(t)
;
ev.preventDefault();
ev.stopPropagation();
resizer.el_rz = ev.target;
resizer.pos = [
ev.clientX - (parseInt(cs.marginLeft) || 0),
ev.clientY - (parseInt(cs.marginTop) || 0),
];
window.addEventListener('mouseup', resizer.end);
window.addEventListener('mousemove', resizer.move);
resizer.overlay.style.display = 'block';
},
init: function () {
resizer.el = document.getElementById("sizer");
resizer.overlay = document.getElementById("overlay");
resizer.el.addEventListener('mousedown', resizer.start);
}
};
function fill_numbers(el) {
let i, s = '';
for (i = 1; i < 500; ++i)
s += (i % 10) + ' ';
el.textContent = s;
}
resizer.init();
spacer.init();
fill_numbers(document.querySelector('.top-line-c'));
fill_numbers(document.querySelector('.left-line-c'));
})();
body {
position : relative;
width : 100vw;
height : 100%;
padding : 0;
margin : 0;
}
#overlay {
position : absolute;
display : none;
z-index : 100;
height : 100vh;
width : 100vw;
opacity : 0.1;
background : #333;
z-index : 100;
cursor : move;
}
.window {
position : absolute;
overflow : hidden;
margin-top : 5px;
margin-left : 5px;
min-width : 10px;
min-height : 10px;
background : #988;
padding : 0;
}
.sizer {
position : relative;
background : #8a8;
bottom : 0;
right : 0;
width : 15px;
height : 15px;
margin-top : 120px;
margin-left : 250px;
cursor : move;
}
.wrap {
position : relative;
background : #565;
width : 100%;
height : 100%;
}
.content-outer {
overflow : hidden;
top : 0;
bottom : 0;
right : 0;
left : 0;
position : absolute;
background : #a77;
padding : 0;
}
.corner {
position : sticky;
width : 25px;
height : 25px;
top : 0;
left : 0;
z-index : 150;
background : red;
}
.top-line {
top : 0;
margin-top : -25px;
margin-left : 25px;
position : sticky;
height : 25px;
background : pink;
width : 100%;
display: table;
white-space : nowrap;
z-index : 100;
overflow : hidden;
}
.left-line {
position : sticky;
max-height : 100%;
overflow : hidden;
background : #494;
width : 25px;
left : 0;
z-index : 100;
text-align : center;
}
.left-line-c {
height : 5000px;
}
.content-inner {
position : absolute;
overflow : scroll;
top : 25px;
right : 0;
bottom : 0;
left : 0;
}
.content-text {
position : absolute;
top : 0;
left : 0;
right : 0;
bottom : 0;
margin : 25px ;
}
.content-spacer {
background: rgb(186,124,13);
background: linear-gradient(90deg, rgba(186,124,13,1) 0%, rgba(112,25,58,0.9037815809917717) 53%, rgba(181,0,255,1) 100%);
margin : 15px;
width : 1000px;
height : 100px;
}
ul {
list-style : none;
margin : 0;
padding : 0;
}
.header {
background : #000;
color : #aaa;
height : 25px;
}
<div id="overlay"></div>
<div class="wrap">
<div class="window">
<div class="content-outer">
<div class="header"><span>Header</span></div>
<div class="content-inner">
<div class="corner">C</div>
<div class="top-line">
<div class="top-line-c"></div>
</div>
<div class="left-line">
<div class="left-line-c"></div>
</div>
<div class="content-text">
<div class="content-spacer">
<ul>
<li><label><input type="radio" name="sp" value="small">small</label></li>
<li><label><input type="radio" name="sp" value="wide" checked>wide</label></li>
<li><label><input type="radio" name="sp" value="high">high</label></li>
<li><label><input type="radio" name="sp" value="big">big</label></li>
</ul>
</div>
</div>
</div>
</div>
<div class="sizer" id="sizer" tabindex="0"></div>
</div>
</div>
I just added display: table; on the .top-line class.

Dynamically Scaling SVG

I'm working on a site where users can manipulate an SVG image through a couple of textboxes.
I would like to have the SVG scale to fit the container div.
For example, if the SVG was exactly the container's height and 10 pixels wide, then doubling the height would cause the apparent width to be 5 pixels.
My page is split roughly in half, with the numbers on the left and the image on the right. Resizing the browser thus causes the SVG's container element to change shape, meaning that I can't hardcode the container's dimensions in the SVG.
Every solution I've found online uses the viewBox attribute; however, I can't find a way to apply that without having a hard-coded container size.
Here is a fiddle with my editor setup:
https://jsfiddle.net/xyjs5b63/
Adjusting viewBox sounds like what you want. I'm not sure what you were doing that made it not work.
var svg = document.querySelector('svg');
var inputs = document.querySelectorAll('input');
var height_elem = inputs[0];
var width_elem = inputs[1];
height_elem.value = '100';
width_elem.value = '100';
height_elem.addEventListener("change", valueChange);
width_elem.addEventListener("change", valueChange);
function valueChange() {
svg.setAttribute('viewBox', "0 0 "+width_elem.value+" "+height_elem.value);
}
valueChange();
#out {
width: 100px;
height: 100px;
background-color: honeydew;
}
svg {
width: 100%;
height: 100%;
}
<div id="main">
<div id="in">
<input type="number"><br>
<input type="number">
</div>
<div id="out">
<svg>
<rect width="100%" height="100%"></rect>
</svg>
</div>
</div>
var rect = document.querySelector('rect');
var svg = document.querySelector('svg');
var inputs = document.querySelectorAll('input');
var height_elem = inputs[0];
var width_elem = inputs[1];
height_elem.value = '100';
width_elem.value = '100';
height_elem.addEventListener("change", valueChange);
width_elem.addEventListener("change", valueChange);
function valueChange() {
max = parseInt(height_elem.value) >= parseInt(width_elem.value) ? 'h' : 'w';
if (max == 'h') {
rect.setAttribute('height', "100%");
rect.setAttribute('width', (width_elem.value * 100 / height_elem.value)+"%");
}
else {
rect.setAttribute('width', "100%");
rect.setAttribute('height', (height_elem.value * 100 / width_elem.value)+"%");
}
}
valueChange();
#main {
width: 100%;
padding: 0;
}
#in {
float: left;
width: 40%;
height: 100%
}
#out {
margin: 10%;
width: 20vw;
height: 20vw;
}
svg {
width: 100%;
height: 100%;
}
<div id="main">
<div id="in">
Height: <input type="number"><br>
Width: <input type="number">
</div>
<br>
<div id="out">
<svg height="auto">
<rect></rect></svg>
</div>
</div>
Does this solve your problem?

Can I do image orientation detection with html or css?

If I have an image on html page, can I use html or css do the following?
When width of the image is greater than height, set height to a fixed value and auto stretch width; when height is greater than width, set width and auto stretch height?
Thanks a lot!
No, this is not possible - conditional statements cannot be handled with HTML or CSS, but you have to do it with JS.
An example would be calculating (and perhaps storing for future use) the aspect ratio of an image to determine whether is it in landscape or portrait mode:
$(document).ready(function() {
$("img").each(function() {
// Calculate aspect ratio and store it in HTML data- attribute
var aspectRatio = $(this).width()/$(this).height();
$(this).data("aspect-ratio", aspectRatio);
// Conditional statement
if(aspectRatio > 1) {
// Image is landscape
$(this).css({
width: "100%",
height: "auto"
});
} else if (aspectRatio < 1) {
// Image is portrait
$(this).css({
maxWidth: "100%"
});
} else {
// Image is square
$(this).css({
maxWidth: "100%",
height: "auto"
});
}
});
});
See fiddle here - http://jsfiddle.net/teddyrised/PkgJG/
2019 update: As ES6 is becoming the defacto standard, the above jQuery code can be easily refactored into vanilla JS:
const images = document.querySelectorAll('img');
Array.from(images).forEach(image => {
image.addEventListener('load', () => fitImage(image));
if (image.complete && image.naturalWidth !== 0)
fitImage(image);
});
function fitImage(image) {
const aspectRatio = image.naturalWidth / image.naturalHeight;
// If image is landscape
if (aspectRatio > 1) {
image.style.width = '100%';
image.style.height = 'auto';
}
// If image is portrait
else if (aspectRatio < 1) {
image.style.width = 'auto';
image.style.maxHeight = '100%';
}
// Otherwise, image is square
else {
image.style.maxWidth = '100%';
image.style.height = 'auto';
}
}
div.wrapper {
background-color: #999;
border: 1px solid #333;
float: left;
margin: 10px;
width: 200px;
height: 250px;
}
<div class="wrapper">
<img src="http://placehold.it/500x350" />
</div>
<div class="wrapper">
<img src="http://placehold.it/350x500" />
</div>
<div class="wrapper">
<img src="http://placehold.it/500x500" />
</div>
However, if all you want is to ensure the image fits within an arbitrary sized container, using simple CSS will work:
div.wrapper {
background-color: #999;
border: 1px solid #333;
float: left;
margin: 10px;
width: 400px;
height: 400px;
}
div.wrapper img {
width: auto
height: auto;
max-width: 100%;
max-height: 100%;
}
<div class="wrapper">
<img src="http://placehold.it/500x350" />
</div>
<div class="wrapper">
<img src="http://placehold.it/350x500" />
</div>
<div class="wrapper">
<img src="http://placehold.it/500x500" />
</div>