Split multiline text into specific-width lines - javascript

I've been trying to have multiple paragraphs with different width but each time a line breaks, the line after goes in a new paragraph <p></p>. I need four of them that I can later style independently.
Here is a screenshot of what I'm trying to achieve, as an exemple:
here is the original question where I found the original script.
What I would like to do:
I've managed to adapt it and understand it quite a bit, to make it work when the lines are all the same. Basically :
<p style="width:700px">I am some ra</p>
<p style="width:700px">ndom text. I</p>
<p style="width:700px">am Some text</p>
<p style="width:700px">.blabla</p>
But now I'm facing an issue:
I'd like to specify different width for the individual lines in my css so the script breaks them perfectly. In other words: A line of text takes all the width of the div paragraph and the remaining text moves into another paragraph (for exemple #text2) until we reach four div paragraphs.
Something like that:
<p style="width:50px">I am some ra</p>
<p style="width:900px">ndom text. I</p>
<p style="width:800px">am Some text</p>
<p style="width:1000px">.blabla</p>
EDIT:
There is some progress so far. The text seems to breaks related to the width of the paragraph just before.
Here is the project:
var p = document.getElementById("p");
var menu = document.getElementsByClassName("menu");
var width = p.clientWidth;
var parent = p.parentNode;
var line = document.createElement("span");
line.style.display = "inline-block";
line.style.visibility = "hidden";
var content = p.innerHTML;
document.body.appendChild(line);
var start = 0;
var i = 1;
let run = 1;
while (i < content.length) {
line.innerHTML = content.substring(start, i);
console.log(i + " " + content.length);
console.log("#text" + run + " width: " + menu[run].clientWidth);
console.log("run: " + run);
if (line.clientWidth > menu[run + 1].clientWidth) {
var newp = document.createElement("p");
newp.id = "text" + run;
newp.className = "menu";
newp.innerHTML = content.substring(start, i - 1);
parent.insertBefore(newp, p);
start = i - 1;
i = start + 1;
run++;
} else {
i++;
}
}
newp = document.createElement("p");
newp.id = "textbis" + run;
newp.className = "menu";
newp.innerHTML = content.substring(start);
parent.insertBefore(newp, p);
parent.removeChild(p);
div {
word-break: break-all;
background-color: lightblue;
width: 700px;
}
p {
background-color: lightgreen;
}
#text0 {
width: 50px;
}
#text1 {
width: 50px;
}
#text2 {
width: 200px;
}
#text3 {
width: 500px;
}
#text4 {
width: 700px;
}
<div>
<p class="menu" id="text1"></p>
<p class="menu" id="text2"></p>
<p class="menu" id="text2"></p>
<p class="menu" id="p">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Saepe,
aliquam? Lorem ipsum dolor sit amet consectetur adipisicing elit.
Quibusdam quaerat iste earum quos eligendi atque aliquam veniam facilis
quis ducimus. Lorem ipsum dolor sit amet consectetur adipisicing elit.
Iure, quisquam! Temporibus consequuntur laboriosam quos odio maxime
iusto at dolore quod ipsa eaque voluptas mollitia, vel odit inventore
sapiente aut!
</p>
</div>

It might not be the best choice performance-wise, but here's one way to do it.
let textarea = document.querySelector("textarea");
let output = document.querySelector("textarea + div");
let widths = [100, 450, 400, 500, 9999];
textarea.addEventListener("input", () => {
output.innerHTML = null;
console.time("split took");
let lines = splitTextByLineWidths(textarea.value, widths);
console.timeEnd("split took");
lines.forEach((line, i) => {
let p = document.createElement("p");
if (widths[i] < window.innerWidth) {
p.style.width = widths[i] + "px";
}
p.textContent = line;
output.append(p);
});
});
textarea.dispatchEvent(new Event("input"));
function splitTextByLineWidths(text, lineWidths) {
let lines = [];
let renderer = document.createElement("div");
renderer.style.position = "absolute";
renderer.style.left = window.innerWidth * -3 + "px";
renderer.style.top = window.innerHeight * -3 + "px";
document.body.appendChild(renderer);
lineWidths.forEach(lineWidth => {
let measure = document.createElement("div");
measure.style.display = "table";
measure.textContent = "dummy";
renderer.appendChild(measure);
let lineHeight = measure.offsetHeight;
let activeText = text;
measure.textContent = activeText;
measure.style.width = lineWidth + "px";
let height = measure.offsetHeight;
while (height > lineHeight) {
activeText = activeText.slice(0, -1);
measure.textContent = activeText;
height = measure.offsetHeight;
}
lines.push(activeText);
text = text.slice(activeText.length);
});
renderer.remove();
return lines;
}
textarea + div {
background-color: lightblue;
padding: 1em;
}
textarea + div p {
background-color: peachpuff;
}
<textarea cols="100" rows="6">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque varius nisi purus, in dignissim justo egestas et. Suspendisse suscipit, metus vitae facilisis cursus, diam est malesuada tellus, eu viverra risus sapien nec neque.</textarea>
<div></div>
This method can be further improved by using smarter ways to trim the text. Currently, it's triming a single character and checking if the text overflows the first line. This is an expensive operation.
One can devise a way to increase the trim length, and instead of going one way (trim only), can go both ways (trim and grow/put back) when the text underflows the width.

Related

Read more read less functionality for mobile view only

I am using read more / read less functionality one of my page, function is performing for desktop and mobile views but I want only for mobile view #media only screen and (max-width:550px), below my current used code.Please help...
function AddReadMore() {
var carLmt = 460;
var readMoreTxt = " ... Read More";
var readLessTxt = " Read Less";
$(".addReadMore").each(function() {
if ($(this).find(".firstSec").length)
return;
var allstr = $(this).text();
if (allstr.length > carLmt) {
var firstSet = allstr.substring(0, carLmt);
var secdHalf = allstr.substring(carLmt, allstr.length);
var strtoadd = firstSet + "<span class='SecSec'>" + secdHalf + "</span><span class='readMore' title='Click to Show More'>" + readMoreTxt + "</span><span class='readLess' title='Click to Show Less'>" + readLessTxt + "</span>";
$(this).html(strtoadd);
}
});
$(document).on("click", ".readMore,.readLess", function() {
$(this).closest(".addReadMore").toggleClass("showlesscontent showmorecontent");
});
}
$(function() {
AddReadMore();
});
.addReadMore.showlesscontent .SecSec,
.addReadMore.showlesscontent .readLess {
display: none;
}
.addReadMore.showmorecontent .readMore {
display: none;
}
.addReadMore .readMore,
.addReadMore .readLess {
font-weight: 100;
margin-left: 2px;
color: #2ab1ce;
cursor: pointer;
}
.addReadMoreWrapTxt.showmorecontent .SecSec,
.addReadMoreWrapTxt.showmorecontent .readLess {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p class="addReadMore showlesscontent"> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam </p>
First: I found two issues:
your click listener should be added directly to the buttons $(".readMore, .readLess").on("click", function() {
for your example the char limit should be smaller than 460 since the example string has only ~160 chars, maybe var carLmt = 60;
You can achieve the media-query-like solution with an additional condition in your string-length-if:
if (allstr.length > carLmt && window.innerWidth <= 550) {
Since it should also work on resize you need a second event listener. In the attached handler you have to reset the paragraphs and call AddReadMore() again. For reseting you first have to delete the two buttons (remove the click-listener before), then grab the inner text of each parargaph and last overwrite with it the old paragraph content for deleting the <span class='SecSec'>.
$(window).on('resize', function() {
$(".readMore, .readLess").off("click").remove();
$('.addReadMore').each(function() {
$(this).html($(this).text());
});
AddReadMore();
});
Working example (resize the window to see the effect):
function AddReadMore() {
var carLmt = 60;
var readMoreTxt = " ... Read More";
var readLessTxt = " Read Less";
$(".addReadMore").each(function() {
if ($(this).find(".firstSec").length)
return;
var allstr = $(this).text();
if (allstr.length > carLmt && window.innerWidth <= 550) {
var firstSet = allstr.substring(0, carLmt);
var secdHalf = allstr.substring(carLmt, allstr.length);
var strtoadd = firstSet + "<span class='SecSec'>" + secdHalf + "</span><span class='readMore' title='Click to Show More'>" + readMoreTxt + "</span><span class='readLess' title='Click to Show Less'>" + readLessTxt + "</span>";
$(this).html(strtoadd);
}
});
$(".readMore, .readLess").on("click", function() {
$(this).closest(".addReadMore").toggleClass("showlesscontent showmorecontent");
});
}
$(function() {
AddReadMore();
});
$(window).on('resize', function() {
$(".readMore, .readLess").off("click").remove();
$('.addReadMore').each(function() {
$(this).html($(this).text());
});
AddReadMore();
});
.addReadMore.showlesscontent .SecSec,
.addReadMore.showlesscontent .readLess {
display: none;
}
.addReadMore.showmorecontent .readMore {
display: none;
}
.addReadMore .readMore,
.addReadMore .readLess {
font-weight: 100;
margin-left: 2px;
color: #2ab1ce;
cursor: pointer;
}
.addReadMoreWrapTxt.showmorecontent .SecSec,
.addReadMoreWrapTxt.showmorecontent .readLess {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p class="addReadMore showlesscontent"> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam </p>
By the way, there are some things that i would make different (optional):
since there is no .firstSec in your example delete the first if
declare the whole buttons as extra vars for readability instead of just their inner text
wrap the whole code in the document ready function
Working example:
$(function() {
function AddReadMore() {
var carLmt = 60;
var readMore = "<span class='readMore' title='Click to Show More'> ... Read More</span>";
var readLess = "<span class='readLess' title='Click to Show Less'> Read Less</span>";
$(".addReadMore").each(function() {
var allstr = $(this).text();
if (allstr.length > carLmt && window.innerWidth <= 550) {
var firstSet = allstr.substring(0, carLmt);
var secdHalf = allstr.substring(carLmt, allstr.length);
var strtoadd = firstSet + "<span class='SecSec'>" + secdHalf + "</span>" + readMore + readLess;
$(this).html(strtoadd);
}
});
$(".readMore, .readLess").on("click", function() {
$(this).closest(".addReadMore").toggleClass("showlesscontent showmorecontent");
});
}
$(window).on('resize', function() {
$(".readMore, .readLess").off("click").remove();
$('.addReadMore').each(function() {
$(this).html($(this).text());
});
AddReadMore();
});
AddReadMore();
});
.addReadMore.showlesscontent .SecSec,
.addReadMore.showlesscontent .readLess {
display: none;
}
.addReadMore.showmorecontent .readMore {
display: none;
}
.addReadMore .readMore,
.addReadMore .readLess {
font-weight: 100;
margin-left: 2px;
color: #2ab1ce;
cursor: pointer;
}
.addReadMoreWrapTxt.showmorecontent .SecSec,
.addReadMoreWrapTxt.showmorecontent .readLess {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p class="addReadMore showlesscontent"> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam </p>

elements.style.backgroundColor returns undefined [duplicate]

This question already has answers here:
Get a CSS value with JavaScript
(8 answers)
What do querySelectorAll and getElementsBy* methods return?
(12 answers)
Closed 3 years ago.
I'm trying to use javascript to see if a class has a specific background color but every time I tried something it always returned undefined. I have tried a lot of things so I'm at a loss here.
CSS
.ChangeColors {
background-color: #ff801b;
color:black;
}
Javascript
function ChangeColors(){
var all = document.getElementsByClassName('ChangeColors');
if (all.style.backgroundColor == "#ff801b") {
for (var i = 0; i < all.length; i++) {
all[i].style.color = 'white';
all[i].style.backgroundColor = 'blue';
}
} else {
for (var i = 0; i < all.length; i++) {
all[i].style.color = 'black';
all[i].style.backgroundColor = '#ff801b';
console.log(help)
}
}
}
You can have seperate classes for changing CSS properties like
.ChangeColors {
width: 100%;
height: 100px;
margin: 10px;
color: black;
}
.orangecolor {
background-color: #ff801b;
}
.bluecolor {
background-color: blue;
color:white;
}
and can loop through nodes and add/remove class according to the requirement
function ChangeColors() {
var all = document.querySelectorAll(".ChangeColors");
for (var i = 0; i < all.length; i++) {
if (all[i].classList.contains("orangecolor")) {
all[i].classList.add("bluecolor");
} else {
all[i].classList.add("orangecolor");
}
}
}
Check fiddle for it
There are 3 problems.
getElementsByClassName returns a list of HTMLCollection
style property is used to get as well as set the inline style of an element.
<div style="color: red;"></div>
Your element does not have inline stlye attribute. So element.style.backgroundColor does not work.
element.style.backgroundColor does not return always HEX color even though you set HEX color. On some browser, it returns RGB color.
This is a bad appraoch. However, if you like it, use window.getComputedStyle(element)["background-color"] and convert it to hex color if it is RGB and then compare.
function ChangeColors() {
var elements = document.getElementsByClassName('change-color');
for (let element of elements) {
var background = window.getComputedStyle(element)["background-color"]
console.log(background)
}
}
ChangeColors()
.change-color {
background-color: ##ff801b;
}
<div class="change-color">
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Iste deserunt officia voluptatum earum laboriosam? Est, ipsam. Ex veniam, incidunt, alias quasi aliquam sit eaque recusandae, expedita fugiat possimus a corrupti.
</div>
You may use two classes for applying color and background.
function ChangeColors() {
var elements = document.getElementsByClassName('change-color');
for (let element of elements) {
if (element.classList.contains("bg-blue")) {
element.classList.add('bg-orange');
} else {
element.classList.add('bg-blue')
}
}
}
ChangeColors()
.bg-blue {
background: blue;
color: white;
}
.bg-orange {
background: #ff801b;
color: black;
}
<div class="change-color">
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Iste deserunt officia voluptatum earum laboriosam? Est, ipsam. Ex veniam, incidunt, alias quasi aliquam sit eaque recusandae, expedita fugiat possimus a corrupti.
</div>
<div class="change-color bg-blue">
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Iste deserunt officia voluptatum earum laboriosam? Est, ipsam. Ex veniam, incidunt, alias quasi aliquam sit eaque recusandae, expedita fugiat possimus a corrupti.
</div>
Read these for more info.
getElementsByClassName
Window.getComputedStyle()
How to compare a backgroundColor in Javascript?
style.backgroundColor doesn't work?
If you use jQuery.
How can I compare two color values in jQuery/JavaScript?
You can also use jQuery for such util functions like below
var color = $('.ChangeColors').css( "background-color" );
Now compare your value with color it should not return undefined
if you don't want to use the whole jQ library you can do it with
getComputedStyle().
let all = document.querySelectorAll(".ChangeColors");
function ChangeColors() {
for (let i = 0; i < all.length; i++) {
const { style } = all[i];
if (
window.getComputedStyle(all[i], false).backgroundColor ===
"rgb(255, 128, 27)"
) {
style.color = "white";
style.background = "blue";
} else {
style.color = "black";
style.background = "#ff801b";
}
}
}
Here is an example https://codesandbox.io/s/eager-feistel-16l4m

How control the position of the "caret navigation/text insertion cursor" inside a text in a editable div?

I have a simple replace function that gets the value input and replace it in the text adding the same value with a span. I'm using it to highlighting the find results, it works fine but the text is an editable div that too have to execute the replace function if typed.
The problem is when the function was executed the caret navigation/text insertion cursor gets back to the start of the text.
There is some manner to control this caret to execute the replace function without changing his position inside of the text?
var txt = document.querySelector("#text");
txt.addEventListener("input", replace);
var input = document.querySelector("#input");
input.addEventListener("input", replace);
function replace() {
let re = new RegExp(input.value, 'gi');
let replace = txt.textContent.replace(re, function (e) {
let r = "<span class='highlight'>" + e + "</span>";
return r;
});
txt.innerHTML = replace;
}
.highlight {
background: #00ff90;
}
#input { padding:5px;
}
#text{
border:1px solid #ccc; padding:5px;
margin-top:5px;
width:300px;
}
The regex works fine
<input id="input" placeholder="regex" type="text" />
<p>
but when you try to type in the text box the function replace was called and the entry text move to the start, the fucntion has to be called but the text cursos should stay still<p>
<div id="text" contenteditable="true">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mattis arcu urna, at volutpat
justo ultrices eget. Ut facilisis congue scelerisque. Vivamus
</div>
<input id="input" placeholder="regex" type="text" />
<div id="text" contenteditable="true">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mattis arcu urna, at volutpat
justo ultrices eget. Ut facilisis congue scelerisque. Vivamus
</div>
<style type="text/css">
.highlight {
background: #00ff90;
}
#input { padding:5px;
}
#text{
border:1px solid #ccc; padding:5px;
margin-top:5px;
width:300px;
}
</style>
<script type="text/javascript">
var txt = document.querySelector("#text");
txt.addEventListener("input", replace);
var input = document.querySelector("#input");
input.addEventListener("input", replace);
var currentInputPos = input.selectionEnd;
function replace() {
let re = new RegExp(input.value, 'gi');
if(input.selectionStart != currentInputPos) {
currentInputPos = input.selectionStart;
let replace = txt.textContent.replace(re, function (e) {
let r = "<span class='highlight'>" + e + "</span>";
return r;
});
txt.innerHTML = replace;
}
}
</script>
I am using this structure and all things looks fine

Move div on mouse move

I am trying to adopt a script I have found in this question, but changing the image to some content seems to be harder than I thought.
The script is quite simple and should make the #content div move inside the holder on mousemove:
// written by Roko C. Buljan
var $mmGal = $('#holder'),
$mmImg = $('#content'),
damp = 10, // higher number = smoother response
X = 0, Y = 0,
mX = 0, mY = 0,
wDiff, hDiff,
zeno;
function motion(){
zeno = setInterval(function(){ // Zeno's paradox "catching delay"
X += (mX-X) / damp;
Y += (mY-Y) / damp;
$mmGal.scrollLeft(X*wDiff).scrollTop(Y*hDiff);
}, 26);
}
// Get image size after it's loaded and run our fn
$mmImg.one('load', function() {
wDiff = ( this.width/$mmGal.width() )-1,
hDiff = (this.height/$mmGal.height())-1;
}).each(function() {
if(this.complete) $(this).load();
});
$mmGal.mousemove(function(e) {
mX = e.pageX-this.offsetLeft;
mY = e.pageY-this.offsetTop;
}).hover(function( e ){
return e.type=='mouseenter'? motion() : setTimeout(function(){
clearInterval(zeno);
},1200); // clear if not used
});
Why doesn't the div #content move (text and image)?
Example JSBIN
I have updated the demo and added content on moving background image.
Check the demo:
$(function(){
var $mmGal = $('#mmGal'),
$mmImg = $('#mmImg'),
damp = 10, // higher number = smoother response
X = 0, Y = 0,
mX = 0, mY = 0,
wDiff, hDiff,
zeno;
function motion(){
zeno = setInterval(function(){ // Zeno's paradox "catching delay"
X += (mX-X) / damp;
Y += (mY-Y) / damp;
$mmGal.scrollLeft(X*wDiff).scrollTop(Y*hDiff);
}, 26);
}
// Get image size after it's loaded and run our fn
$mmImg.one('load', function() {
wDiff = ( this.width/$mmGal.width() )-1,
hDiff = (this.height/$mmGal.height())-1;
}).each(function() {
if(this.complete) $(this).load();
});
$mmGal.mousemove(function(e) {
mX = e.pageX-this.offsetLeft;
mY = e.pageY-this.offsetTop;
}).hover(function( e ){
return e.type=='mouseenter'? motion() : setTimeout(function(){
clearInterval(zeno);
},1200); // clear if not used
});
});
*{margin:0;padding:0;}
.main-wrapper {
position: relative;
width:150px;
height:150px;
}
#mmGal{
position:relative;
margin: 20px auto;
width:412px;
height:220px;
overflow:hidden;
background:#eee;
z-index: 0;
}
.content {
position: absolute;
left: 0;
right: 0;
top:0;
color: #ffffff;
padding: 10px;
z-index: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="main-wrapper">
<div class="content">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. quae molestias ad dolores qui blanditiis, Quas eaque soluta quia ipsa? aliquam?</p>
</div>
<div id="mmGal">
<img id="mmImg" src="http://www.wired.com/images_blogs/rawfile/2013/11/offset_WaterHouseMarineImages_62652-2-660x440.jpg">
</div>
</div>
My original script uses a .load() for the underlay image to load. Since you don't have a background image to move - you don't need necessarily that code part.
Calculate w/hDiff inside the mousemove event.
Also, use jQuery's .outerWidth() and .innerWidth() for the calculation:
jQuery(function($) {
const $mm = $('#holder'),
$mmCont = $('#content'),
damp = 10; // 1 = immediate, higher number = smoother response
let X = 0, Y = 0,
mX = 0, mY = 0,
wDiff = 0, hDiff = 0,
zeno, tOut;
$mm.on({
mousemove(ev) {
wDiff = ($mmCont.innerWidth() / $mm.outerWidth()) -1,
hDiff = ($mmCont.innerHeight() / $mm.outerHeight()) -1;
mX = ev.pageX - this.offsetLeft;
mY = ev.pageY - this.offsetTop;
},
mouseenter() {
clearTimeout(tOut);
clearInterval(zeno);
zeno = setInterval(function() { // Zeno's paradox "catching delay"
X += (mX - X) / damp;
Y += (mY - Y) / damp;
// Use CSS transition:
// $mmCont.css({ transform: `translate(${-X * wDiff}px, ${-Y * hDiff}px)` });
// Use Scroll:
$mm[0].scrollTo(X * wDiff, Y * hDiff);
}, 26);
},
mouseleave() {
// Allow the image to move for some time even after mouseleave
tOut = setTimeout(function() {
clearInterval(zeno);
}, 1200);
}
});
});
#holder {
background: gray;
width: 100%;
position: relative;
overflow: hidden;
height: 180px;
}
#content {
width: 150%;
height: 600px;
background: lightgray;
padding: 40px;
}
<div id="holder">
<div id="content">
<div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus dolore impedit dignissimos porro repellendus numquam aut quibusdam, consequuntur modi facere? Totam ut libero corporis sit sequi explicabo ab magni quaerat unde animi aliquid facere
necessitatibus, quae molestias ad dolores qui blanditiis, quisquam minima beatae autem iure. Neque animi tempore iste accusamus ut cum ipsam possimus, perspiciatis quia illo obcaecati sed molestiae amet architecto, nostrum cumque quaerat minima
minus, consequatur rem error nihil. Ipsa eveniet, praesentium suscipit optio blanditiis at, vel illum harum omnis quam. Quas eaque soluta quia ipsa? Illum inventore veritatis facilis eveniet voluptatibus atque laborum necessitatibus adipisci aliquam?</div>
<img src="https://i.stack.imgur.com/BfcTY.jpg">
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

Prevent divs from colliding

I have a game where users can chat. Every speech is appended to body as position absolute element with user x position as "left" CSS tag. They are also animated to go to top. I don't want them to have a colliding, vertically or horisontally.
Here's an example:
They don't have to be one on another, but they need to be "one after one"
I have tried a jquery each() and then remove 60 pixel from the current speech position. Here's my code:
var Speech = {
History: [],
New: function(user, text, int) {
var pos = getUserPosition(user).x + 18;
var speech = HTML.Speech.split('\n');
var maxInt = speech.length - 1;
if (int <= maxInt) {
var html = speech[int];
var random = Rand(10);
Speech.pushThemUp();
/** append here ect...
set left position...
$('#speech' + random).css("left", nLeft + "px"); **/
}
},
pushThemUp: function() {
$('.speech').each(function(i, obj) {
var newTop = parseInt($(this).css('top')) - 60;
$(this).css('top', newTop+'px');
});
},
Listener: function() {
var int = setInterval(function() {
$('.speech').each(function(i, obj) {
if(parseInt($(this).css('top')) < 0) {
$(this).remove();
} else {
var newTop = parseInt($(this).css('top')) - 10;
$(this).animate({'top': newTop+'px'});
}
});
}, 1000);
},
getHistory: function() {
return Speech.History;
}
};
Speech.Listener();
module.exports = Speech;
But it doesn't work. They can still have colliding like the example up.
How can I solve that?
Please note: in that example, Speech.Listener() wasn't called.
EDIT: finally, I think my current solution to loop over .speech class and then add top px is good, but why is it animated? Look at the gif, pushThemUp function don't have to animate the speech bubbles but directly edit position, how can I solve that?
I created a code snippet where messages appear and move upwards. When there's no space left, a scrollbar appears.
var box = document.getElementById("box");
var topp = 3;
function post(str) {
var obj = document.createElement("div");
obj.className = "chatObj";
obj.innerHTML = str;
box.appendChild(obj);
box.appendChild(document.createElement("br"));
var width = obj.clientWidth;
var height = obj.clientHeight;
obj.style.marginLeft = (box.clientWidth / 2 - width / 2) + "px";
var x = 15;
obj.style.top = (box.clientHeight - x - height) + "px";
var interval, ctop;
interval = setInterval(function() {
x += 4;
console.log(ctop, topp);
ctop = box.clientHeight - x - height;
obj.style.top = ctop + "px";
if (ctop <= topp) {
obj.style.top = topp + "px";
topp += height + 6;
clearInterval(interval);
}
}, 5);
}
setTimeout(function() {
post("Hi!");
}, 500);
setTimeout(function() {
post("Trollolo!");
}, 1500);
setTimeout(function() {
post("Lorem ipsum dolor sit amet, timeam aliquando ei cum, putent possim in usu, at causae pericula petentium has. In mea legere salutatus voluptaria. No vix ancillae accusata. Nec meis probo noster eu, ius no quas audire.<br><br>Qui quem nominavi ei. Pri nisl eirmod id. Has cetero vocent abhorreant no, at mei altera expetendis. Has id novum aeterno salutatus.<br><br>Prompta offendit et eos, eos an admodum comprehensam, ex velit doming dolorem mei. At has dolor alterum laoreet, id duo tollit libris contentiones. An mel recteque omittantur dissentiet, ex nam novum iuvaret. Per id alterum habemus gubergren.<br><br>Nulla possim mea in. Vis et postulant constituam. Viris vulputate vituperatoribus eu usu, wisi meis ex his. Prompta accumsan cum et, possim eligendi omittantur sed id. Eos ad nemore integre recusabo, agam doctus viderer ei pri, cu eius nonumes senserit vis. Qui iudico causae te.<br><br>Eam ne mandamus evertitur, case adversarium neglegentur duo ex, no cum nominati forensibus. Et vel putant deleniti. Illum aliquando voluptatibus per no, ei quo albucius phaedrum. Cu lorem appetere percipit sed, ubique epicuri ad eos, te eos diam nusquam persecuti. Eu qui meis illum eleifend, eam veniam vivendo no, nisl fierent in quo.");
}, 2500);
#box {
display: block;
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
background-color: tomato;
overflow: auto;
}
#box .chatObj {
position: absolute;
display: inline-block;
max-width: 80%;
background-color: white;
border-radius: 2px;
padding: 3px 6px;
margin: 3px 0;
box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.1);
font-family: Arial, sans-serif;
font-size: 14px;
}
#box .chatObj::after {
display: block;
position: absolute;
content: ".";
color: transparent;
height: 6px;
}
<div id="box"></div>
If you use position: relative and a wrapper element with display: block, you can get exactly what you want without having to worry about collision detection. The only thing you'll need to do is calculate the initial top value based on the players position minus the initial top position of the bubble (since using position: relative the bubbles will be added to the DOM bellow the last bubble).
See this simple jsfiddle. https://jsfiddle.net/straker/0n6jbupg/

Categories