I have a contenteditable div and I want the site to count each row of text the user has in the div similar to many coding IDEs. (Example image below to show what I mean:)
How would I go about doing this?
You can accomplish this with two container elements and a little bit of scripting:
$(document).ready(function(){
$('#edit').css('min-height', $('#edit').height());
$('#edit').html('');
var currentHeight = $('#edit').height();
var lineHeight = currentHeight;
$('#edit').keyup(function(){
if($(this).height()!=currentHeight){
currentHeight = $(this).height();
var lines = currentHeight/lineHeight;
$('#nums').html('')
for (i = 1; i < lines+1; i++) {
$('#nums').append('<span>'+i+'</span>')
}
}
});
});
#container{
border: 2px solid gray;
display: flex;
width: 200px;
}
#nums{
width: 25px;
background-color: lightgrey;
}
#nums span{
width: 100%;
display: block;
text-align: center;
}
#edit{
display: inline-block;
width: 100%;
}
#editwrapper{
width: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="container">
<div id="nums">
<span>1</span>
</div>
<div id="editwrapper">
<div id="edit" contenteditable="true">
filler
</div>
</div>
</div>
That's not so simple, actually. If a user typed in your div and the text wrapped, JavaScript doesn't know that it wrapped; there's no row to count!
This solution is super specific, and I'll leave showing the line numbers to you:
<div contenteditable="true" id="myDiv" style="width:300px;font-family:monospace" onchange="myHandler()"></div>
<script>
const limit = 40 // number of monospace chars to fill a row
let count = 0
function myHandler (e) {
let lines = Math.ceil(e.target.value.length / limit)
if (lines !== count) {
showlines(lines) // your function to display the line #s
count = lines
}
}
</script>
Takeaway: your div should be a fixed width and you should count how many monospace chars fit within that width. Then display those lines.
Related
I'm making a chess game with the HTML5 canvas and I'm having trouble getting the rank and file labels to display correctly. By labels I mean the 1,2,..,8 and A,B,...H text that is positioned around the outskirts of the board to uniquely identify board positions. Currently I'm using two divs outside of the "board" element to display the the labels. The way I currently do this isn't responsive and often doesn't display properly when shown on devices other than the one I designed it on. The text for the file (letters) and rank (numbers) might be different sizes, or the characters might not align with the center of each column or row.
This is what the board looks like when everything is working as intended
And here's the problem I run into on the phone. The letters don't line up with the tiles of the board, as well as the control widget moving outside the page:
chessboard = document.getElementById('chessboard');
ctxPiece = document.getElementById('chesspieceCanvas').getContext('2d');
ctxHighlight = document.getElementById('highlight').getContext('2d');
drawBoard(chessboard, chessboard.getContext('2d'));
function drawBoard(canvas, ctx) {
var rows = 8;
TILE_SIZE = canvas.height / rows;
var white = true;
var TILE_COLOR1 = "rgb(160,82,45)";
var TILE_COLOR2 = "rgb(245,222,179)";
for (var row = 0; row < rows; row++) {
for (var col = 0; col < rows; col++) {
if (!white) {
ctx.fillStyle = TILE_COLOR1;
ctx.fillRect(col * TILE_SIZE, row * TILE_SIZE, TILE_SIZE, TILE_SIZE);
white = true;
} else {
ctx.fillStyle = TILE_COLOR2;
ctx.fillRect(col * TILE_SIZE, row * TILE_SIZE, TILE_SIZE, TILE_SIZE);
white = false;
}
}
white = !white;
}
}
#gameArea {
width: 650px;
font-size: 16px;
}
#output {
text-align: center;
}
#outputMessage {
display: inline-block;
font-size: 2.5em;
font-weight: bold;
}
#boardAndInfo {
overflow: visible;
position: relative;
width: 1000px;
}
#board {
display: inline-block;
border: 0.12em solid black;
height: inherit;
position: relative;
width: 600px;
line-height: 0;
overflow: hidden;
}
#chessboard {
position: absolute;
z-index: 0;
}
#highlight {
position: absolute;
z-index: 1;
}
#gameInfo {
display: inline-block;
height: 585px;
width: 190px;
vertical-align: top;
position: relative;
}
#fileNotation {
color: gray;
font-size: 2.0em;
margin-left: 50px;
letter-spacing: 54px;
}
#chesspieceCanvas {
font-size: 70px;
font-family: 'Open Sans', sans-serif;
position: relative;
z-index: 2;
}
#rankNotation {
color: gray;
float: left;
font-size: 2.0em;
line-height: 235%;
position: relative;
width: 1.5ch;
word-wrap: break-word;
}
<body>
<div id='gameSynopsis'>
</div>
<div id="gameArea" lang='en'>
<div id="output">
<span id="outputMessage">Some text here</span>
<span id="aiThinkingIndicator" class="loading hide"></span>
</div>
<div id="fileNotation" class="boardLabel">
ABCDEFGH
</div>
<div id="rankNotation" class="boardLabel">
87654321
</div>
<div id="boardAndInfo">
<div id="board">
<canvas id="chessboard" width="600" height="600"></canvas>
<canvas id="highlight" width="600" height="600"></canvas>
<canvas id="chesspieceCanvas" width="600" height="600">
<p id='canvasSupportMsg'></p>
</canvas>
</div>
<fieldset id="gameInfo">
<legend id='gameInfoTitle'>Stuff</legend>
</fieldset>
</div>
</div>
</body>
The fiddle for my game can be found here. If I'm missing anything else please let me know.
I'm looking for a way to change the way this works in order to have it scale properly but I haven't been able to find a solution yet.
Here's what I ultimately ended up doing and I'm pretty happy with the result. I wasn't able to figure out a solution using straight HTML and CSS but hopefully this might be of use to someone else down the road.
I created wrappers for the individual components of the rank and file and applied styles to each element to set the spacing between them. The individual element wrappers for the letters and numbers are formatted as they are to eliminate the white-space that is created between inline elements by default.
<div id="rank" class="boardLabel">
<div class="rankNumber">8</div>
<div class="rankNumber">7</div>
<div class="rankNumber">6</div>
<div class="rankNumber">5</div>
<div class="rankNumber">4</div>
<div class="rankNumber">3</div>
<div class="rankNumber">2</div>
<div class="rankNumber">1</div>
</div>
<div id="fileNotation" class="boardLabel">
<span class="fileLetter">A
</span><span class="fileLetter">B
</span><span class="fileLetter">C
</span><span class="fileLetter">D
</span><span class="fileLetter">E
</span><span class="fileLetter">F
</span><span class="fileLetter">G
</span><span class="fileLetter">H
</span>
</div>
The following Javascript/jQuery code is then used to set the spacing
// to set the rank spacing
var tileSize = $('#board').height() / 8;
$('.rankNumber').css('line-height', tileSize + 'px');
// to set file spacing
$('#file > .fileLetter').width($('#board').width() / 8);
Essentially the board width is divided by 8 to calculate the width of each file element (as chess is played on an 8x8 grid), with a similar process occurring for calculating the height. This calculation is then used to set the value for the relevant CSS properties. For the file I added a left-margin with the same magnitude as the width of the rank so that it aligned properly with the start of the board
If one would like more information on the issue it can be obtained from the Stack Overflow question, How to remove the space between inline-block elements?.
I would like to display divs when the div contents before is full of words and continue to fill this new div with the rest of the words.
I don't know how to do it. In fact, in the code below I wrote that the div is displayed on click of a button. I also can't set the "fill-action" explained above.
The limit of the words in one div has to be settable from the code.
For example, if I set the limit to two words and there are only two words to be displayed, the second div shouldn't be created.
But If there are four words to be displayed and the limit is still on two words,
the second div has to be created and has to be filled with the third and fourth words.
Another problem is that if I write HTML text (e.g. <font color="#ff0000">), the tags (e.g. <font) shouldn't be considered as a word.
Jsfiddle
HTML:
<div id="faketxt" contenteditable>Write Here</div>
<button id='btn'>OK</button>
<div id='casella' class='fakes'></div>
CSS:
#faketxt {
-moz-appearance: textfield-multiline;
-webkit-appearance: textarea;
border: 1px solid gray;
height: 28px;
overflow: auto;
padding: 2px;
resize: both;
width: 400px;
}
#casella{
width: 150px;
height: 300px;
font-size: 10px;
border-style: solid;
}
.fakes{
width: 150px;
height: 300px;
font-size: 10px;
border-style: solid;
}
JQUERY:
$('#btn').click(function() {
var primo = document.getElementById('faketxt');
var secondo = document.getElementById('casella');
secondo.innerHTML = primo.innerHTML;
var myDiv = $('#casella');
myDiv.text(myDiv.text().substring(0,5)) //This is when the div is "full"
});
document.getElementById("btn").onclick = function () {
var ok = true;
if (ok === true) {
var div = document.createElement('div');
div.className = 'fakes';
document.getElementsByTagName('body')[0].appendChild(div);
}
};
In this case I set that the div is full when there are 5 letters, so the word "Here" has to be displayed in the second div...
Is this possible?
I can't figure it out.
for displaying divs at right position
css:
.fakes{
width: 150px;
height: 300px;
font-size: 10px;
border-style: solid;
display : inline-block;
}
#boxes{
display : flex;
}
HTML
<div id="faketxt" contenteditable>Write Here</div>
<button id='btn'>OK</button><br>
<div id="boxes">
<div id='casella' class='fakes'></div>
</div>
Use String.split() to separate the words (by spaces) and add a div container for each word using Array.foreach(). Also with this approach, use Array.shift() to set the text of the myDiv element (i.e. with id="casella") to the first word.
UPDATE:
Per the changing requirements, the code below now has a number input for the word limit. It then strips HTML codes (using the HTML entities) using a regular expression and then uses a counter to add words to newly created div elements. The functionality to create a new div element has been abstracted to the function createdDiv().
$('#btn').click(function() {
var primo = document.getElementById('faketxt');
var wordLimit = $('#wordLimit').val();
//strip html characters from string and use a regular expression
//to split based on white-space characters
var words = primo.innerHTML.replace(/(<([^>]+)>)/ig,"").split(/\s/);
if (words.length) {
var count = 0;
var div = createDiv();
words.forEach(function(word) {
if (++count > wordLimit) {
count = 0; //reset counter
div = createDiv();
}
if (div.innerHTML) {
div.append(' ');
}
div.append(word);
});
}
});
function createDiv() {
div = document.createElement('div'); //could use jQuery $('div') instead
div.className = 'fakes';
document.body.append(div);
return div;
}
#faketxt {
-moz-appearance: textfield-multiline;
-webkit-appearance: textarea;
border: 1px solid gray;
height: 28px;
overflow: auto;
padding: 2px;
resize: both;
width: 400px;
}
#casella {
width: 150px;
height: 300px;
font-size: 10px;
border-style: solid;
}
.fakes {
width: 150px;
height: 300px;
font-size: 10px;
border-style: solid;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>Word Limit:
<input type="number" id="wordLimit" value="1" />
</div>
<div id="faketxt" contenteditable>Write Here</div>
<button id='btn'>OK</button>
var myDiv = $('#casella');
var primo = document.getElementById('faketxt');
var secondo = document.getElementById('casella');
$('#btn').click(function() {
var inputArray = primo.innerHTML.split(" ");
var secDivContent = '';
if(inputArray[0].length > 5 || primo.innerHTML.length > 5 ) {
secDivContent = primo.innerHTML.substr(5);
}
var div = document.createElement('div');
div.className = 'fakes';
div.innerHTML = secDivContent;
document.getElementsByTagName('body')[0].appendChild(div);
});
I am using CSS Flex in order to display three DIVs consequential in a wrapper.
The width dimension for the first DIV (item0) is to 50px.
I need to change the height of the wrapper and keep the original width for item0 and item1 DIVs inside the wrapper.
My current problem is that:
When re-sizing the wrapper, item for flex (item0) get a different width. size
Please click the two buttons in the example to see the change in dimension.
I would like to know:
Why the width size change?
How to maintain the width size for item0 and item2same as my original CSS settings?
Notes:
I understand scroll-bar appears taking space. How I could I keep item0 and item2 at a fix width and item1 stretching to fill up the remaining space? (I have tried to use % but I cannot get the result wanted).
var btn0 = document.getElementById('btn0');
var btn1 = document.getElementById('btn1');
var item0 = document.getElementById('item0');
var result = document.getElementById('result');
var wrapper = document.getElementById('wrapper');
btn0.addEventListener('click', function(event) {
wrapper.style.height = '25px';
result.value = item0.getBoundingClientRect().width;
});
btn1.addEventListener('click', function(event) {
wrapper.style.height = '350px';
result.value = item0.getBoundingClientRect().width;
});
#wrapper {
width: 250px;
height: 350px;
background-color: gray;
overflow: auto;
}
#flex-container {
display: flex;
}
#item0 {
width: 50px;
background-color: red;
height: 150px;
}
#item1 {
width: 150px;
background-color: orange;
height: 150px;
}
#item2 {
width: 50px;
background-color: pink;
height: 150px;
}
<div id="wrapper">
<div id="flex-container">
<div id="item0" class="item">a
</div>
<div id="item1" class="item">b
</div>
<div id="item2" class="item">c
</div>
</div>
</div>
<button id='btn0' type="button">Smaller wrapper</button>
<button id='btn1' type="button">Bigger wrapper</button>
item0 width is: <input id="result"type="text">
Use flex: 0 0 50px for the item0 style.
See jsfiddle
It tells the flexbox layout don't grow and don't shrink and give it a width of 50px.
It is always good to use the flex: property for flexbox items because the default value for it may be different from browser to browser.
(Actually your problem doesn't happen in firefox for example)
When the width of the container is reduced, all child elements are reduced proportionally too.
Add flex: 0 0 auto; to item0 and item2. It disallows element to shrink to its minimum when there is not enough space.
var btn0 = document.getElementById('btn0');
var btn1 = document.getElementById('btn1');
var item0 = document.getElementById('item0');
var result = document.getElementById('result');
var wrapper = document.getElementById('wrapper');
btn0.addEventListener('click', function(event) {
wrapper.style.height = '25px';
result.value = item0.getBoundingClientRect().width;
});
btn1.addEventListener('click', function(event) {
wrapper.style.height = '350px';
result.value = item0.getBoundingClientRect().width;
});
#wrapper {
width: 250px;
height: 350px;
background-color: gray;
overflow: auto;
}
#flex-container {
display: flex;
}
#item0 {
flex: 0 0 auto;
width: 50px;
background-color: red;
height: 150px;
}
#item1 {
width: 150px;
background-color: orange;
height: 150px;
}
#item2 {
flex: 0 0 auto;
width: 50px;
background-color: pink;
height: 150px;
}
<div id="wrapper">
<div id="flex-container">
<div id="item0" class="item">a
</div>
<div id="item1" class="item">b
</div>
<div id="item2" class="item">c
</div>
</div>
</div>
<button id='btn0' type="button">Smaller wrapper</button>
<button id='btn1' type="button">Bigger wrapper</button>
item0 width is: <input id="result"type="text">
Here is an example chat app ->
The idea here is to have the .messages-container take up as much of the screen as it can. Within .messages-container, .scroll holds the list of messages, and in case there are more messages then the size of the screen, scrolls.
Now, consider this case:
The user scrolls to the bottom of the conversation
The .text-input, dynamically gets bigger
Now, instead of the user staying scrolled to the bottom of the conversation, the text-input increases, and they no longer see the bottom.
One way to fix it, if we are using react, calculate the height of text-input, and if anything changes, let .messages-container know
componentDidUpdate() {
window.setTimeout(_ => {
const newHeight = this.calcHeight();
if (newHeight !== this._oldHeight) {
this.props.onResize();
}
this._oldHeight = newHeight;
});
}
But, this causes visible performance issues, and it's sad to be passing messages around like this.
Is there a better way? Could I use css in such a way, to express that when .text-input-increases, I want to essentially shift up all of .messages-container
2:nd revision of this answer
Your friend here is flex-direction: column-reverse; which does all you ask while align the messages at the bottom of the message container, just like for example Skype and many other chat apps do.
.chat-window{
display:flex;
flex-direction:column;
height:100%;
}
.chat-messages{
flex: 1;
height:100%;
overflow: auto;
display: flex;
flex-direction: column-reverse;
}
.chat-input { border-top: 1px solid #999; padding: 20px 5px }
.chat-input-text { width: 60%; min-height: 40px; max-width: 60%; }
The downside with flex-direction: column-reverse; is a bug in IE/Edge/Firefox, where the scrollbar doesn't show, which your can read more about here: Flexbox column-reverse and overflow in Firefox/IE
The upside is you have ~ 90% browser support on mobile/tablets and ~ 65% for desktop, and counting as the bug gets fixed, ...and there is a workaround.
// scroll to bottom
function updateScroll(el){
el.scrollTop = el.scrollHeight;
}
// only shift-up if at bottom
function scrollAtBottom(el){
return (el.scrollTop + 5 >= (el.scrollHeight - el.offsetHeight));
}
In the below code snippet I've added the 2 functions from above, to make IE/Edge/Firefox behave in the same way flex-direction: column-reverse; does.
function addContent () {
var msgdiv = document.getElementById('messages');
var msgtxt = document.getElementById('inputs');
var atbottom = scrollAtBottom(msgdiv);
if (msgtxt.value.length > 0) {
msgdiv.innerHTML += msgtxt.value + '<br/>';
msgtxt.value = "";
} else {
msgdiv.innerHTML += 'Long long content ' + (tempCounter++) + '!<br/>';
}
/* if at bottom and is IE/Edge/Firefox */
if (atbottom && (!isWebkit || isEdge)) {
updateScroll(msgdiv);
}
}
function resizeInput () {
var msgdiv = document.getElementById('messages');
var msgtxt = document.getElementById('inputs');
var atbottom = scrollAtBottom(msgdiv);
if (msgtxt.style.height == '120px') {
msgtxt.style.height = 'auto';
} else {
msgtxt.style.height = '120px';
}
/* if at bottom and is IE/Edge/Firefox */
if (atbottom && (!isWebkit || isEdge)) {
updateScroll(msgdiv);
}
}
/* fix for IE/Edge/Firefox */
var isWebkit = ('WebkitAppearance' in document.documentElement.style);
var isEdge = ('-ms-accelerator' in document.documentElement.style);
var tempCounter = 6;
function updateScroll(el){
el.scrollTop = el.scrollHeight;
}
function scrollAtBottom(el){
return (el.scrollTop + 5 >= (el.scrollHeight - el.offsetHeight));
}
html, body { height:100%; margin:0; padding:0; }
.chat-window{
display:flex;
flex-direction:column;
height:100%;
}
.chat-messages{
flex: 1;
height:100%;
overflow: auto;
display: flex;
flex-direction: column-reverse;
}
.chat-input { border-top: 1px solid #999; padding: 20px 5px }
.chat-input-text { width: 60%; min-height: 40px; max-width: 60%; }
/* temp. buttons for demo */
button { width: 12%; height: 44px; margin-left: 5%; vertical-align: top; }
/* begin - fix for hidden scrollbar in IE/Edge/Firefox */
.chat-messages-text{ overflow: auto; }
#media screen and (-webkit-min-device-pixel-ratio:0) {
.chat-messages-text{ overflow: visible; }
/* reset Edge as it identifies itself as webkit */
#supports (-ms-accelerator:true) { .chat-messages-text{ overflow: auto; } }
}
/* hide resize FF */
#-moz-document url-prefix() { .chat-input-text { resize: none } }
/* end - fix for hidden scrollbar in IE/Edge/Firefox */
<div class="chat-window">
<div class="chat-messages">
<div class="chat-messages-text" id="messages">
Long long content 1!<br/>
Long long content 2!<br/>
Long long content 3!<br/>
Long long content 4!<br/>
Long long content 5!<br/>
</div>
</div>
<div class="chat-input">
<textarea class="chat-input-text" placeholder="Type your message here..." id="inputs"></textarea>
<button onclick="addContent();">Add msg</button>
<button onclick="resizeInput();">Resize input</button>
</div>
</div>
Side note 1: The detection method is not fully tested, but it should work on newer browsers.
Side note 2: Attach a resize event handler for the chat-input might be more efficient then calling the updateScroll function.
Note: Credits to HaZardouS for reusing his html structure
You just need one CSS rule set:
.messages-container, .scroll {transform: scale(1,-1);}
That's it, you're done!
How it works: First, it vertically flips the container element so that the top becomes the bottom (giving us the desired scroll orientation), then it flips the content element so that the messages won't be upside down.
This approach works in all modern browsers. It does have a strange side effect, though: when you use a mouse wheel in the message box, the scroll direction is reversed. This can be fixed with a few lines of JavaScript, as shown below.
Here's a demo and a fiddle to play with:
//Reverse wheel direction
document.querySelector('.messages-container').addEventListener('wheel', function(e) {
if(e.deltaY) {
e.preventDefault();
e.currentTarget.scrollTop -= e.deltaY;
}
});
//The rest of the JS just handles the test buttons and is not part of the solution
send = function() {
var inp = document.querySelector('.text-input');
document.querySelector('.scroll').insertAdjacentHTML('beforeend', '<p>' + inp.value);
inp.value = '';
inp.focus();
}
resize = function() {
var inp = document.querySelector('.text-input');
inp.style.height = inp.style.height === '50%' ? null : '50%';
}
html,body {height: 100%;margin: 0;}
.conversation {
display: flex;
flex-direction: column;
height: 100%;
}
.messages-container {
flex-shrink: 10;
height: 100%;
overflow: auto;
}
.messages-container, .scroll {transform: scale(1,-1);}
.text-input {resize: vertical;}
<div class="conversation">
<div class="messages-container">
<div class="scroll">
<p>Message 1<p>Message 2<p>Message 3<p>Message 4<p>Message 5
<p>Message 6<p>Message 7<p>Message 8<p>Message 9<p>Message 10<p>Message 11<p>Message 12<p>Message 13<p>Message 14<p>Message 15<p>Message 16<p>Message 17<p>Message 18<p>Message 19<p>Message 20
</div>
</div>
<textarea class="text-input" autofocus>Your message</textarea>
<div>
<button id="send" onclick="send();">Send input</button>
<button id="resize" onclick="resize();">Resize input box</button>
</div>
</div>
Edit: thanks to #SomeoneSpecial for suggesting a simplification to the scroll code!
Please try the following fiddle - https://jsfiddle.net/Hazardous/bypxg25c/. Although the fiddle is currently using jQuery to grow/resize the text area, the crux is in the flex related styles used for the messages-container and input-container classes -
.messages-container{
order:1;
flex:0.9 1 auto;
overflow-y:auto;
display:flex;
flex-direction:row;
flex-wrap:nowrap;
justify-content:flex-start;
align-items:stretch;
align-content:stretch;
}
.input-container{
order:2;
flex:0.1 0 auto;
}
The flex-shrink value is set to 1 for .messages-container and 0 for .input-container. This ensures that messages-container shrinks when there is a reallocation of size.
I've moved text-input within messages, absolute positioned it to the bottom of the container and given messages enough bottom padding to space accordingly.
Run some code to add a class to conversation, which changes the height of text-input and bottom padding of messages using a nice CSS transition animation.
The JavaScript runs a "scrollTo" function at the same time as the CSS transition is running to keep the scroll at the bottom.
When the scroll comes off the bottom again, we remove the class from conversation
Hope this helps.
https://jsfiddle.net/cnvzLfso/5/
var doScollCheck = true;
var objConv = document.querySelector('.conversation');
var objMessages = document.querySelector('.messages');
var objInput = document.querySelector('.text-input');
function scrollTo(element, to, duration) {
if (duration <= 0) {
doScollCheck = true;
return;
}
var difference = to - element.scrollTop;
var perTick = difference / duration * 10;
setTimeout(function() {
element.scrollTop = element.scrollTop + perTick;
if (element.scrollTop === to) {
doScollCheck = true;
return;
}
scrollTo(element, to, duration - 10);
}, 10);
}
function resizeInput(atBottom) {
var className = 'bigger',
hasClass;
if (objConv.classList) {
hasClass = objConv.classList.contains(className);
} else {
hasClass = new RegExp('(^| )' + className + '( |$)', 'gi').test(objConv.className);
}
if (atBottom) {
if (!hasClass) {
doScollCheck = false;
if (objConv.classList) {
objConv.classList.add(className);
} else {
objConv.className += ' ' + className;
}
scrollTo(objMessages, (objMessages.scrollHeight - objMessages.offsetHeight) + 50, 500);
}
} else {
if (hasClass) {
if (objConv.classList) {
objConv.classList.remove(className);
} else {
objConv.className = objConv.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
}
}
}
}
objMessages.addEventListener('scroll', function() {
if (doScollCheck) {
var isBottom = ((this.scrollHeight - this.offsetHeight) === this.scrollTop);
resizeInput(isBottom);
}
});
html,
body {
height: 100%;
width: 100%;
background: white;
}
body {
margin: 0;
padding: 0;
}
.conversation {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100%;
position: relative;
}
.messages {
overflow-y: scroll;
padding: 10px 10px 60px 10px;
-webkit-transition: padding .5s;
-moz-transition: padding .5s;
transition: padding .5s;
}
.text-input {
padding: 10px;
-webkit-transition: height .5s;
-moz-transition: height .5s;
transition: height .5s;
position: absolute;
bottom: 0;
height: 50px;
background: white;
}
.conversation.bigger .messages {
padding-bottom: 110px;
}
.conversation.bigger .text-input {
height: 100px;
}
.text-input input {
height: 100%;
}
<div class="conversation">
<div class="messages">
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is the last message
</p>
<div class="text-input">
<input type="text" />
</div>
</div>
</div>
You write;
Now, consider this case:
The user scrolls to the bottom of the conversation
The .text-input, dynamically gets bigger
Wouldn't the method that dynamically sets the .text-input be the logical place to fire this.props.onResize().
To whom it may concern,
The answers above did not suffice my question.
The solution I found was to make my innerWidth and innerHeight variable constant - as the innerWidth of the browser changes on scroll to adapt for the scrollbar.
var innerWidth = window.innerWidth
var innerHeight = window.innerHeight
OR FOR REACT
this.setState({width: window.innerWidth, height: window.innerHeight})
In other words, to ignore it, you must make everything constant as if it were never scrolling. Do remember to update these on Resize / Orientation Change !
IMHO current answer is not a correct one:
1/ flex-direction: column-reverse; reverses the order of messages - I didn't want that.
2/ javascript there is also a bit hacky and obsolete
If you want to make it like a PRO use spacer-box which has properties:
flex-grow: 1;
flex-basis: 0;
and is located above messages. It pushes them down to the chat input.
When user is typing new messages and input height is growing the scrollbar moves up, but when the message is sent (input is cleared) scrollbar is back at bottom.
Check my snippet:
body {
background: #ccc;
}
.chat {
display: flex;
flex-direction: column;
width: 300px;
max-height: 300px;
max-width: 90%;
background: #fff;
}
.spacer-box {
flex-basis: 0;
flex-grow: 1;
}
.messages {
display: flex;
flex-direction: column;
overflow-y: auto;
flex-grow: 1;
padding: 24px 24px 4px;
}
.footer {
padding: 4px 24px 24px;
}
#chat-input {
width: 100%;
max-height: 100px;
overflow-y: auto;
border: 1px solid pink;
outline: none;
user-select: text;
white-space: pre-wrap;
overflow-wrap: break-word;
}
<div class="chat">
<div class="messages">
<div class="spacer-box"></div>
<div class="message">1</div>
<div class="message">2</div>
<div class="message">3</div>
<div class="message">4</div>
<div class="message">5</div>
<div class="message">6</div>
<div class="message">7</div>
<div class="message">8</div>
<div class="message">9</div>
<div class="message">10</div>
<div class="message">11</div>
<div class="message">12</div>
<div class="message">13</div>
<div class="message">14</div>
<div class="message">15</div>
<div class="message">16</div>
<div class="message">17</div>
<div class="message">18</div>
</div>
<div class="footer">
<div contenteditable role="textbox" id="chat-input"></div>
</div>
<div>
Hope I could help :)
Cheers
I'm creating a map generator that lets a user enter the number of rows and columns in the map, and then create that map (as divs). Everything works correctly, except the divs, instead of being in rows and columns, it's all in one big column. It does have the correct amount of tiles (for example, it has 6 if you enter 3 and 2, or 25 if you enter 5 and 5). The kicker is that if I enter divs in the regular html file, they line up like they're supposed to.
Here's my javascript function:
function createMap(rows, columns) {
var $div = $('<div></div>');
var k = 0;
while (k < rows) {
for (i = 0; i < columns; i++) {
$div.append('<div></div>');
}
$div.append('<br>');
k++;
}
$('body').empty();
$('body').append($div);
}
Here's my CSS:
div {
width: 100px;
height: 100px;
border: 1px solid black;
display: inline-block;
}
The generated divs are reading the CSS fine - they have the proper height, width, and border. It seems that they're just creating a new line for every one. Can anyone tell me why?
You gave ALL your divs a width of 100px. You have divs in a div. So you need to change your styles to only target the children
You want only the children divs to have the width of 100px.
function createMap(rows, columns) {
var $div = $('<div></div>');
var k = 0;
while (k < rows) {
for (i = 0; i < columns; i++) {
$div.append('<div></div>');
}
$div.append('<br>');
k++;
}
$('body').empty();
$('body').append($div);
}
createMap(2,3)
div > div {
width: 100px;
height: 100px;
border: 1px solid black;
display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Float your divs
div {
width: 100px;
height: 100px;
border: 1px solid black;
display: inline-block;
float: left;
}