Update 3 (Demo 3)
https://i.sstatic.net/V8YJ5.gif
Modifications
I have observed that in the most current OP code, there is no longer a use of position: relative
, which is a positive change. However, I think one thing has been overlooked:
<span class='pagebreak spacer'
contenteditable="false"></span>
I believe you initially included contenteditable="false"
to provide additional functionality to your .pagebreak
s and prevent them from being deleted, so I have re-added them.
Comparative Analysis
Demo 3 displays my solution alongside the OP's code for comparison. In addition, Demo 3 showcases 2 buttons (one for each content editor) that highlight each <span>
of text. Below is a list of classes from the OP's code (the content editor on the right) and their equivalent classes in my code (content editor on the left).
div.textframe
................section.editor
p.textOutline
................article.content
span.flowbox.spacer
......mark.vertRule
span.pagebreak.spacer
..mark.breaker
The OP has two specific requirements:
Clicking the empty areas around the <span>s
should move the cursor to the corner of the content area.
Ensuring the number of characters per line remains consistent with the OP's current capacity.
This issue has persisted for years, but its root cause remains unclear. By treating this anomaly as a behavior pattern, we can potentially address it by introducing alternative behaviors.
Demo 2 and Demo 3 comply with these criteria by implementing the following style rulesets:
Demo 2
article p {display: table;...
Demo 3
.content {display:table-cell;...
The behavior of table-cells is rigid and well-established, and to my knowledge, they are the only non-replaced element that inherently conforms to its content and surrounding table elements. Additionally, an element with display: table-cell
does not require nesting inside a <tr>
within a <table>
.
Demo 3
.content { display: table-cell;...
/* Begin Defaults */
* {
margin: 0;
padding: 0;
border: 0;
box-sizing: border-box;
}
html,
body {
background: white;
font: 400 16px/1.45 Arial;
height: 100%;
width: 100%;
}
/* End Defaults */
/* Begin Optional Layout */
#page01 {
display: flex;
flex-wrap: wrap;
justify-content: space-evenly;
align-items: flex-start;
background: rgba(45, 99, 198, 0.6);
margin: 0 auto 20px;
height: fit-content;
min-width: 100%
}
/* End Optional Layout */
/* Begin Primary Styles */
.editor {
width: 350px;
height: 600px;
border: 1px solid black;
background: #fff;
}
.vertRule {
float: right;
clear: right;
width: 30px;
height: 600px;
}
.content {
display: table-cell;
word-break: break-word;
}
mark {
display: block;
pointer-events: none;
}
.break {
min-height: 80px;
}
/* End Primary Styles */
<!-- Details skipped for brevity -->
/* End Demo Test */
<!-- Begin Demo Test -->
<input id="outline1" type='checkbox' hidden>
<label for='outline1' class='btn'>Outline 1</label>
<input id="outline2" type='checkbox' hidden>
<label for='outline2' class='btn'>Outline 2</label>
<hr>
<!-- End Demo Test -->
<!-- Begin Optional Layout Part 1 -->
<main id='page01'>
<!-- End Optional Layout Part 1 -->
<!-- Begin Primary Markup -->
<section class="editor" contenteditable='true'>
<mark class="vertRule" contenteditable='false'></mark>
<article class='content'>
<!-- Content here -->
</article>
</section>
<!-- More details omitted -->
</main>
<!-- End Optional Layout Part 2 -->
Update 2 (Demo 2)
Regarding Demo 1:
"you solved it for my contrived example, yes. Unfortunately it is not possible to set those values on the elements in the actual app, the flow gets totally out of wack there."
Check out Demo 2; it performs better than Demo 1. By utilizing only positioned elements, conflicts in flow are avoided. To adapt Demo 2 to your application, simply add position:relative
to the parent elements. The relevant style is specified below:
article p {display: table;...
It was crucial to assign position:relative
to everything nested within .textframe
to ensure proper interaction between static and positioned elements. Tables and their components adhere to certain rules that govern not only their content but also their relationships with neighboring elements.
Demo 2
article p {display: table...
.container {
width: 400px;
float: left
}
.textframe {
width: 350px;
height: 650px;
outline: 2px dotted lightblue;
overflow: hidden;
margin: 0 15px 0 0;
/* Needed for long words */
word-break: break-word;
}
.textframe article {
position: relative;
height: 650px;
}
article p {
display: table;
margin: 0;
position:relative;
}
.flowbox {
width: 2px;
height: 650px;
float: right;
clear: right;
outline: 1px solid red;
}
.pagebreak {
display: block;
pointer-events:none;
position:relative;
}
<div class="container">
<h4>
article p {display: table; position: relative;}<br>
all children of .textframe have: position: relative;
</h4>
<div class="textframe a">
<div class="flowbox"></div>
<article contenteditable="true">
<p>
<!-- Nested span elements -->
</p>
<hr>
</article>
</div>
</div>
References
MDN - Float
MDN - Position
CSS Tricks - Absolute Positioning Inside Relative Positioning
CSS Tricks - All About Floats
display: table/table-cell
word-break:break-word