Efficient way to recalculate/reflow a grid of absolutely positioned elements - javascript

Problem Statement
I have a grid, the element corners may or may not align but all the edges touch so there are no gaps. There are also no overlaps. For example, it's possible to have something like this:
+----+-------+
| | B |
| +---+---+
| A | C | |
| |---| D |
| | E | |
+----+---+---+
The grid is created via absolutely positioned elements. (I realize it may be easier to create such grid via a tree instead, where the parent node is the container forming a rectangle with neighboring element(s), but I think that may limit the ways in which I'd be able to resize elements - I'll explain later).
I want to be able to resize a single element and have neighboring elements recompute their dimensions such that they snap to the new element dimensions without leaving gaps. For example, let's assume we're resizing element C:
If I resize left edge of C towards A, I want A to shrink horizontally. Since A shrinks, both B and E have to expand towards A to fill that void.
If I resize bottom edge of C down, E should shrink, no other elements should be affected.
If I resize right edge of C into D, D should shrink, E and C should grow into that void.
If I resize top edge of C into B, B should shrink vertically and D should expand with C.
Why Tree Structure Won't Work
Now, as mentioned before, I realize that nesting these elements inside container elements (a tree-like structure) would handle the above case much easier. The reason I'm thinking a tree structure won't work for me (in addition to the fact that I already have too much code relying on absolute positions) is that I don't want the following case's resizing be dependent on the underlying tree structure that happens to be underneath:
+---+---+---+
| | | |
+---+---+---+
| | | |
+---+---+---+
| | | |
+---+---+---+
With a tree, this example wouldn't work, as the middle tile resizing would resize elements that happen to share the same parent/container, even if they don't need to resize.
Current Thoughts/Work
I'm trying to figure out how to compute which additional elements need to be resized in an efficient way for my absolute elements. I'm thinking of something along the following lines:
After resize that grows the element in a given direction, take the corresponding edge and perform document.elementsFromPoint() along this edge in a binary search pattern from one corner to another until the element returned for the min point is the same as that for the max point for every sampled point (if they're not the same, sample a new point at the midpoint and continue doing so recursively). This set of elements will contain all the elements that the element has invaded as a result of it's resizing (so they need to be shrunk by the opposite edge)
After a resize that shrinks the element, perform the same kind of binary edge traversal along the original edge (before the resize), but a couple pixels in the opposite direction from the resize (this should hit the elements that need to grow to fill the gap)
For the main element, it will be either one or the other bullet above (shrinking or growing), but the next step now is finding "side-effects", if the edge of the neighboring element goes beyond the edge of the original element, the same kind of analysis must be performed along this extension. This in turn may cause new side-effects along the same edge if we have a brick-like pattern.
The search explained in first bullet would be something like this, and then I would check for side-effects after:
function binarySearch(min, max, resizedElement, otherCoord, vertical=false) {
function getElement(x, y) {
if (vertical) {
let tmp = x;
x = y;
y = tmp;
}
// we know there will always be an element touching, so this
// should only throw an error if we pass bad otherCoord
return document.elementsFromPoint(x, y).filter(e => e !== resizedElement)[0];
}
let elements = new Set(),
startIndex = min,
startElement= getElement(min, otherCoord),
stopIndex = max,
stopElement = getElement(max, otherCoord);
if (startElement === stopElement) {
elements.add(startElement);
} else {
let middle = Math.floor((stopIndex + startIndex)/2),
left = binarySearch(min, middle, resizedElement, otherCoord, vertical),
right = binarySearch(middle, max, resizedElement, otherCoord, vertical);
elements = new Set([...elements, ...left, ...right]);
}
return elements;
}
Am I over-complicating this? Is there a better approach? Is this doable via trees and I'm just not seeing it?

If the underlying structure doesn't change then you can probably solve your problem with a tree structure css flexbox.
Flexbox is a very powerful layout tool that is native to modern browser engines. You use css to declare your layout using display: flex; among other simple css.
CSS trick's flexbox tutorial can explain it much better than I can so please refer to this to understand what's going on. The code below is more of a demo.
The idea is to alter the flexbox styles of the element. To resize the element, change the flex-basis using javascript. I just have buttons below to show the proof of concept but ultimately, you want to use mouse events to resize the elements. You can divide the event.clientX by the container width (container.clientWidth) to get a percentage of where the mouse is relative to the container and use that value for a flexbasis.
In the demo below, I'm using one variable to that I use to keep track of the flexbasis of the element .a and .a-complement. When you click the buttons, the flexbasis updates for each element. They both start off at 50% 50% and the grow/shrink by 10% which each button press. This example could be expanded to encompass resizing all the elements using the same technique. They would all respect each other's sizes and they would all have no gaps etc.
Moral of the story: let the layout engine do the work for you! Don't use absolute positioning unless you really have to.
To address the tree structure issues: you could restructure the tree moving divs into other divs when needed. If this complicates things too much then unfortunately the browser may not have native support for your document structure.
But it might in the future...
If flexbox doesn't solve your issue then the more experimental CSS GRID might, but note that CSS grid is only implemented in the lastest browser and no mobile browsers which might be okay given your target audience.
let aBasis = 0.5;
const elementA = document.querySelector('.a');
const aComplement = document.querySelector('.a-complement');
document.querySelector('#left').addEventListener('click', () => {
aBasis -= 0.1;
elementA.style.flexBasis = (aBasis * 100) + '%';
aComplement.style.flexBasis = ((1 - aBasis) * 100) + '%';
console.log((aBasis * 100) + '%', ((1 - aBasis) * 100) + '%');
});
document.querySelector('#right').addEventListener('click', () => {
aBasis += 0.1;
elementA.style.flexBasis = (aBasis * 100) + '%';
aComplement.style.flexBasis = ((1 - aBasis) * 100) + '%';
console.log((aBasis * 100) + '%', ((1 - aBasis) * 100) + '%');
});
.a {
display: flex;
background-color: red;
flex: 1;
justify-content: center;
align-items: center;
}
.b {
display: flex;
background-color: blue;
flex: 1;
justify-content: center;
align-items: center;
}
.c {
display: flex;
background-color: green;
flex: 1;
justify-content: center;
align-items: center;
}
.d {
display: flex;
background-color: yellow;
flex: 1;
justify-content: center;
align-items: center;
}
.e {
display: flex;
background-color: orange;
flex: 1;
justify-content: center;
align-items: center;
}
.h-container {
display: flex;
align-items: stretch;
flex: 1;
}
.v-container {
display: flex;
flex: 1;
flex-direction: column;
}
.container {
display: flex;
height: 200px;
width: 200px;
flex: 1
}
.example-container {
display: flex;
width: 100%;
}
<div class="example-container">
<div class="container">
<div class="h-container">
<div class="a">
<span>A</span>
</div>
<div class="a-complement v-container">
<div class="b">
<span>B</span>
</div>
<div class="h-container">
<div class="v-container">
<div class="c"><span>C</span></div>
<div class="e"><span>E</span></div>
</div>
<div class="d"><span>D</span></div>
</div>
</div>
</div>
</div>
<div>
<button id="left">move a to the left</button>
<button id="right">move a to the right</button>
</div>
</div>

Related

Waiting to render another elements

I attached an image to can exemplify the problem. I have 4 elements in page:
a table (which is an header with some data)
a chart
a table under the chart (which use chart labels as table header)
a footer
Because first table have only 2 rows (and is static), tableChart also is static (with only 5 rows) and the footer have only one row, I want to calculate dynamically the height of the chart.
So, I get window.height (or container) and decrease table height, tableChart height and footer height. Then, assign the value to chart element. I do this inside AfterViewInit lifecycle (I tried also DoCheck), but can't solve a problem: because #chart element is rendering in same time with the rest, when I get #chartTable height inside setChartHeight() method, it get only a part of total height necessary to render all 5 rows, so the chart will have a bigger height than the necessary.
To be more specific:
chartTable have an normal height of 100px;
because the setChartHeight() is called too early, chartTable is found having only 20px (I think can render only first row)
so, from the total of 500px (example) will decrease only 20px, instead of 100px and the chart height will be 480px (instead of 400px)
How can I wait to render all elements out of chart, and just at the final, to calculate the height of it in a right mode.
ngAfterViewInit(): void {
this.setChartHeight();
}
setChartHeight(): void {
let container = document.getElementById('container');
if (typeof container == 'undefined' || container == null) {
return;
}
let table = document.getElementById('table');
let chartTable = document.getElementById('chartTable');
let footer = document.getElementById('footer');
const height = container.clientHeight - table.clientHeight - chartTable.clientHeight -
footer.clientHeight;
let chart = document.getElementById('chart');
chart.style.height = height + 'px';
}
Another problem which I think exists is that chart and chartTable have same data source. So, when the source will came, because the chart element is above chartTable element, it will be rendered first. So, I need to render chartonly when I'm sure thatchartTable` is already displayed on the page.
edit: codesandbox example
thanks
Finally, how Strella and Liam said, I found a solution using css and avoiding reuse flex-layout library.
.container {
height: 100%;
display: flex;
flex-flow: column;
}
.table {
flex: 0 1 auto;
}
.chart-container {
flex: 1 1 auto;
display: flex;
flex-flow: column;
&__chart {
flex: 1 1 auto;
}
&__table {
flex: 0 1 100px;
}
}

Dynamically adjust a div's css zoom to fit container's size

Suppose such html code:
<editor>
<tree></tree>
</editor>
In my application, the tree is used to store user's input, for example:
'123'
'1111111111111111111111111111111111111111111111111111111'
So overflow is possible if text is too long.
I'd like to apply a css 'zoom' style to tree, to ensure it's size is smaller than editor.
How can I calculate the prefect zoom, using JavaScript?
You can effectively just scale it down step by step until it fits in the container.
Effectively this is:
Styling the elements so they will naturally overflow
Stepping down the scale 5% at a time
Stopping once the child element is smaller than it's parent
function calcSize() {
// The elements we need to use and our current scale
var editor = document.getElementById("editor")
var tree = document.getElementById("tree")
var scale = 1;
// Reset the initial scale and style incase we are resizing the page
tree.classList.add("loading");
tree.style.transform = "scale(1)";
// Loop until the scale is small enough to fit it's container
while (
(editor.getBoundingClientRect().width <
tree.getBoundingClientRect().width) &&
(scale > 0) // This is just incase even at 0.05 scale it doesn't fit, at which point this would cause an infinate loop if we didn't have this check
) {
// Reduce the scale
scale -= 0.05;
// Apply the new scale
tree.style.transform = "scale(" + scale + ")";
}
// Display the final result
tree.classList.remove("loading");
console.log("Final scale: " + Math.round(scale * 100) / 100)
}
// Run on load and on resize
calcSize();
window.addEventListener("resize", calcSize);
#editor {
display: block;
max-width: 50%;
font-size: 20px;
border: 1px solid #000;
overflow: visible;
}
#tree {
display: inline-block;
white-space: nowrap;
/* This is important as the default scale will be relative to the overflowed size */
transform-origin: 0 50%;
}
#tree.loading {
opacity: 0;
}
<editor id="editor">
<tree id="tree" class="loading">This is some overflowing text This is some overflowing text.</tree>
</editor>
(Try viewing the snippet in fullscreen and resizing the window to see it in effect)

Trigger IntersectionObserver when element is already in viewport

The IntersectionObserver is triggered when an element is visible in the viewport for a certain amount (0-100%). This means, when the element is already 100% in the viewport it does not trigger anymore, as there is no change on the threshold.
I have a element that has a height of 200vh and I want the IntersectionObserver to trigger, when I scroll over this element. So the element is always 100% inside the viewport.
Is there a way to trigger the observer while scrolling over the element?
I cannot use the scroll event, as I am using a CSS scroll-snap, which causes the event to be swallowed by the browser, before JS can detect it.
Hopefully I was able to grasp your challenge here, so I'll attempt to propose a solution that should work for your use case, even though there's no code to use as a reference.
From my understanding you're using scroll-snap to snap sections as the user interacts by doing scroll and your intention is to have the Intersection Observer to trigger as the users move from section to section.
In the following example you'll see how sections are being snapped but the debugger shows which section is being shown to the user.
const debuggerSpan = document.querySelector('#current-section');
const sections = [...document.querySelectorAll('.scroll-snap-item')];
const div = document.querySelector('.scroll-snap-container');
/*
* This method will get called any time a section touches the top
* of the viewport.
*/
const intersectionDetected = (entries, observer) => {
entries.forEach((entry) => {
const {
innerText
} = entry.target;
if (!entry.isIntersecting) return;
// Making it obvious that the current section is correct.
debuggerSpan.innerText = innerText;
});
};
const observer = new IntersectionObserver(intersectionDetected, {
/*
* Root should be div and not the default (doc).
*/
root: div,
/*
* Negative margin from the bottom creates an invisible line
* to detect intersections.
*
* The reason why the recommendation is to use -1% and -99% is to
* avoid the race condition between two intersections happening
* (AKA the section about to be out of the viewport and the section
* about to enter the viewport).
*/
rootMargin: '-1% 0% -99% 0%',
/*
* Default value but making it explicit as this is the
* only configuration that works.
*/
threshold: 0
});
sections.forEach(section => observer.observe(section));
.scroll-snap-item {
height: 100vh;
display: grid;
place-items: center;
font-size: 4rem;
scroll-snap-align: start;
}
.scroll-snap-container {
scroll-snap-type: y mandatory;
overflow-y: scroll;
height: 100vh;
}
/* Decorative stuff below*/
.scroll-snap-item:nth-child(odd) {
background-color: gray;
}
aside {
position: fixed;
font-size: 1.6rem;
bottom: 16px;
right: 16px;
background-color: #333;
color: white;
padding: 16px;
border-radius: 32px;
}
<div class="scroll-snap-container">
<section class="scroll-snap-item">1</section>
<section class="scroll-snap-item">2</section>
<section class="scroll-snap-item">3</section>
<section class="scroll-snap-item">4</section>
<section class="scroll-snap-item">5</section>
<section class="scroll-snap-item">6</section>
</div>
<aside>Current section: <span id="current-section"></span></aside>
I wrote a couple of practical posts that cover what's behind all this and what was the thought process to address this situation. Please feel free to give it a read and leave a comment if things are not clear enough:
Scrollspying made easy with the Intersection Observer API.
A graphical introduction to the Intersection Observer API.
Both are quick reads and should provide everything you need to tackle this and even more complex problems with the Intersection Observer. Also, feel free to play around with this tool I wrote called The Intersection Observer Playground where you can try out different configurations and see how they affect the intersection triggers.
Hope this is helpful!

Programmatically Resizing Divs

I'm working on an HTML5 browser game that can be divided into 3 parts: two UI panels on the left and right of a center set of square canvases for the playing surface. The three panels need to be horizontally aligned, and the total game needs to keep an aspect ratio of 16:9. The left and right panels should be of equal widths, and all three panels must be of equal height. I have specified a minimum width and height inside a resize() function called when an onresize event is detected.
Currently, each panel is a div, and all three are contained inside a section. Right now, the section isn't necessary, but I want to keep the game separated from extra content at the bottom of the screen that I might choose to add later.
The CSS style is as follows:
* {
vertical-align: baseline;
font-weight: inherit;
font-family: inherit;
font-style: inherit;
font-size: 100%;
border: 0 none;
outline: 0;
padding: 0;
margin: 0;
}
#gameSection {
white-space: nowrap;
overflow-x: hide;
overflow-y: hide;
}
#leftPanel, #centerPanel, #rightPanel {
display: inline-block;
}
#leftPanel {
background-color: #6495ed;
}
#centerPanel {
background-color: #e0ffff;
}
#rightPanel {
background-color: #b0c4de;
Right now, I have set the background color of each div just to show me when I'm correctly setting the size of each div.
The body of my HTML document is as follows:
<body onresize="resize()">
<section id="gameSection">
<div id="leftPanel">Left Panel.</div>
<div id="centerPanel">Center Panel.</div>
<div id="rightPanel">Right Panel.</div>
</section>
</body>
And finally, my resize() function (I created a separate function for resizing the game in case I add more elements below later):
function resize() {
var MIN_GAME_WIDTH = 800;
var MIN_GAME_HEIGHT = 450;
var GAME_ASPECT_RATIO = 16 / 9;
var width = window.innerWidth;
var height = window.innerHeight;
var gWidth, gHeight;
if(width < MIN_GAME_WIDTH || height < MIN_GAME_HEIGHT) {
gWidth = MIN_GAME_WIDTH;
gHeight = MIN_GAME_HEIGHT;
}
else if ((width / height) > GAME_ASPECT_RATIO) {
<!-- width is too large for height -->
gHeight = height;
gWidth = height * GAME_ASPECT_RATIO;
}
else {
<!-- height is too large for width -->
gWidth = width;
gHeight = width / GAME_ASPECT_RATIO;
}
resizeGame(gWidth, gHeight, GAME_ASPECT_RATIO);
}
function resizeGame(var gWidth, var gHeight, var aspectRatio) {
var gSection = document.getElementById("gameSection");
var lPanel = document.getElementById("leftPanel");
var cPanel = document.getElementById("centerPanel");
var rPanel = document.getElementById("rightPanel");
gSection.height = gHeight;
gSection.width = gWidth;
<!-- should the below be taken care of in the CSS? -->
lPanel.height = gHeight;
cPanel.height = gHeight;
rPanel.height = gHeight;
cPanel.width = cPanel.height;
lPanel.width = (gWidth - cPanel.width) / 2;
rPanel.width = lPanel.width;
}
I've tried a number of different commands to resize the divs, but it just isn't working for me. When I try adding test canvases, color appears, but the boxes still aren't the correct size. I have also considered loading an invisible background image to each div and scaling it to the desired size; however, I was able to resize my canvas using the above method before and it seemed to work just fine.
Additional Notes
While I've already had pretty good success resizing a single canvas, I don't want to use just one canvas for the game because not all parts of the UI need to be drawn at the same time.
I'm trying to keep this solely in Javascript.
I suspect that I could just use CSS to handle resizing by fixing the aspect ratio to 16:9 and using width:56.25% for the center panel and width:21.875% for the side panels, but that limits me to one aspect ratio and doesn't explain why my above script isn't working.
I can provide the entire HTML file if needed. This is what it's supposed to look like:
End Goal (without right panel)
Thank you!
UDPATE:
jsfiddle
I got it kind of working here. I made a lot of changes/minor fixes to the code before finding what was wrong (other than various syntax errors):
You were using .width and .height instead of .style.width and .style.height, and you were applying integers to these instead of strings with "px" appended to them. Both of these things are completely understandable to miss.
I also moved the onresize from the body tag into the JS, don't know why it wasn't working on jsfiddle, but this is good practice anyways.
In the future: learn how to debug JS using the console and when you ask questions, use small examples, not your entire codebase. This question could have been simplified to "How do I resize a div?" with one line of JS and one div. You also should consider not doing this specific thing in JS, and using flexbox as redbmk said.

Scale fixed-width font to fit constant number of characters on a line

I have a websocket based terminal session. I want the font size to increase to fill the div such that it is always 80 characters wide. What's the best way of doing this? I'd love it if there was a CSS-only way of doing this, but I'm already using jQuery, so a vanilla javascript or jQuery solution would be good too.
Here's a CSS-only example based on the8472's answer:
div,textarea{
width:100%;
max-width: 100%;
box-sizing: border-box;
-moz-box-sizing: border-box;
font-family: "Lucida Console", Monaco, monospace;
font-size: 1.99vw; /* The text overflows in safari using (int)2 */
white-space: nowrap;
overflow-x:hidden;
/* styling */
background:#09f;
min-height: 100px;
margin-bottom: 30px;
}
How does it work?
To get the right font-size, divide 160 by the number of characters you want to fit per line, minus 0.01.
http://jsfiddle.net/mb2L4mee/1/
Tested in Chrome, FF and safari.
For browsers that support fractional font sizes or CSS text-rendering: geometricPrecision, you can get a fixed-width font to perfectly fit within any bounds.
In this screenshot, the fourth line is exactly 80 characters:
For browsers that don't support fractional font sizes , it is impossible to always make x characters fit within a given width.
Here's the same text with a 12px font:
… and with a 13px font:
Using the 12px font, the longest line is 83 characters, so it's too small. Using the 13px font, the longest line is 77 characters, so it's too large.
In this situation, you must shrink or widen the container to match the font:
The code below places an 80-character wide span in divs of varying widths, using a style of word-wrap: break-word;. It does a binary search for the best font size, using getClientRects() to determine upper and lower bounds for the binary search.
In case the browser doesn't support fractional font sizes, it adjusts the container's width to match the font size.
The span is then removed.
$('div').each(function() {
var x= $('<span>'+Array(80).join('x')+'</span>').appendTo(this),
lo= 0.1,
hi= 50,
mid;
do {
mid= (lo+hi)/2;
$(this).css({
fontSize: mid
});
if(x[0].getClientRects().length > 1) {
hi= mid;
}
else {
lo= mid;
}
} while(Math.abs(hi-lo)>0.001);
while(x[0].getClientRects().length === 1) {
$(this).css({
width: '-=1'
});
}
while(x[0].getClientRects().length === 2) {
$(this).css({
width: '+=1'
});
}
x.remove();
});
Tested in Chrome, IE, Firefox, Safari, and Opera.
Fiddle
One approach would be to calculate the size of a 80 char line for a fixed font-size and register a function on resize event to adjust the font-size based on the terminal size.
<div id="terminal">
<div class="text">
Some text
</div>
</div>
function scale(lineWith) { /* lineWidth based on 80char 10px font-size */
var ratio = $('#terminal').width() / lineWith;
$('.text').css('font-size', (10 * ratio) + 'px');
}
$(window).resize(function(){
scale(lineWith)
});
Demo: http://jsfiddle.net/44x56zsu/
This seems to adjust pretty good in Chrome (Versión 39.0.2171.99 m)
But I don't think it's possible to adjust the font-size to exactly fit the container size for all browsers because decimal values are rounded.
Font size test: http://jsfiddle.net/ahaq49t1/
This rounding issue in some browsers prevents to do an exact adjustment using font properties.
Within limits it might be possible in pure CSS. If you know the fraction of the viewport that the div will occupy then you can scale the font based on the view port width.
.terminal {font-size: 1vw;} would result in a font size (vertical) equivalent to 1% of the view port. Since a monospace font also has a fixed aspect ratio that results in a width depending on the aspect ratio of the font and the view port size.
Not sure if this is the most elegant solution, and it needs a bit more polishing too. But you can measure the font. Create a hidden measure bar to see how wide the font is in pixels.
Fill it with the desired amount of characters (using 20 here for demonstration purposes):
<span id="measureBar">12345678901234567890</span>
Keep increasing the font size of the measure bar and find the largest width that's still not wider than the div:
$(
function() {
var $measureBar = $('#measureBar');
var $console = $('#console');
$measureBar.css('font-size', '1px');
for (i = 1; $measureBar.width() <= $console.width(); i++) {
$measureBar.css('font-size', i + 'px');
}
$console.css('font-size', (i - 1) + 'px');
}
)
#console {
background-color: black;
color: white;
}
#measureBar {
display: none;
}
#measureBar,
#console {
font-family: courier;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span id="measureBar">12345678901234567890</span>
<div id="console">12345678901234567890 Text that should be twenty characters wide.</div>
This can be done using pure css using the vw (viewport width), vh (viewport height), vmin (relative to width or height, whichever is smaller), vmax (relative to width or height, whichever is larger) length Units.
For browser compatibility you can check this out.
For a simple tutorial check this out.
I was facing a very similar requirement and came up with a fairly straight forward solution. As soon as I get some down time, I'll be writing a jQuery plugin around this example code.
As was mentioned in some of the other answers here, the key is using em as your font-size unit. Additionally, to achieve a similar layout in all (modern) browsers, for a solution that must cope with various fixed-width fonts, the ruler method is the most reliable approach that I have found.
function fixieRemeasure() {
var offset = document.getElementById("fixie").offsetWidth,
ruler = (document.getElementById("fixieRuler").offsetWidth * 1.01),
fontSize = (offset / ruler);
document.getElementById("fixie").style.fontSize = fontSize + "em";
}
function fixieInit(columns) {
document.getElementById("fixieRuler").innerText =
new Array(columns + 1).join("X").toString();
fixieRemeasure();
}
(function () {
fixieInit(80);
window.onresize = function () { fixieRemeasure(); };
} ());
#fixie {
padding: 0 2px 0 6px;
font-size: 1em;
background-color: #000;
color: #0F0;
}
#fixieRuler {
font-size: 1em;
visibility: hidden;
}
<code id="fixieRuler"></code>
<div id="fixie">
<pre>+------------------------------------------------------------------------------+
| |
| ,%%%, |
| ,%%%` %==-- |
| ,%%`( '| |
| ,%%# /\_/ |
| ,%.-"""--%%% "##__ |
| %%/ |__`\ |
| .%'\ | \ / // |
| ,%' > .'----\ | [/ |
| < <<` || |
| `\\\ || |
| )\\ )\ |
| ^^^jgs^^"""^^^^^^""^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| ASCII Art: http://www.chris.com/ascii/index.php?art=animals/horses |
+------------------------------------------------------------------------------+</pre>
</div>
This is only a proof of concept and there are a few fairly obvious issues baked in. Mainly, the resize method is called continuously and that should be replaced with something like onResizeEnd and properly attached to the DOM prior to production use. To componentize, I'd use a class selector instead of an id, and would also dynamically insert/remove the ruler element on-the-fly.
Hope it helps.
UPDATE
I created a new project, fixie.js on github, based on this example code. Still has a bit of work to be done in terms of performance and modularization, but it is works well in all browsers and I believe that it provides the simplest means to solve the issue with a minimal amount of boilerplate code.
The only requirements are that you set a font-size to your pre elements in em, and provide them a class name with the format of 'fixie_COLS':
<pre class="fixie_80">I'll be 80 columns</pre>
jQuery & webcomponent implementations to follow. It's MIT licensed and any contributions are welcome: https://github.com/dperish/fixie.js

Categories