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>
Related
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.
Starting from the code found at https://www.w3schools.com/howto/howto_js_collapsible.asp, I would like to create a collapsible menu which would work also for nested content.
var coll = document.getElementsByClassName("collapsible");
var i;
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function() {
this.classList.toggle("active");
var content = this.nextElementSibling;
if (content.style.maxHeight){
content.style.maxHeight = null;
} else {
content.style.maxHeight = content.scrollHeight + "px";
}
});
}
.collapsible {
background-color: #777;
color: white;
cursor: pointer;
padding: 18px;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
}
.active, .collapsible:hover {
background-color: #555;
}
.collapsible:after {
content: '\002B';
color: white;
font-weight: bold;
float: right;
margin-left: 5px;
}
.active:after {
content: "\2212";
}
.content {
padding: 0 18px;
max-height: 0;
overflow: hidden;
transition: max-height 0.2s ease-out;
background-color: #f1f1f1;
}
<button class="collapsible">Open Collapsible</button>
<div class="content">
<button class="collapsible">Open Collapsible</button>
<div class="content">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</div>
<button class="collapsible">Open Collapsible</button>
<div class="content">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</div>
</div>
The code above works for the first (root) collapsible.
However, when the child collapsible are expanded, there is no space enough for seeing their content.
Only if they are kept opened, closing the root collapsible and reopening it will show the child content correctly.
I know the problem is in that when the root collapsible is expanded, its content maxHeight is set to scrollHeight + "px";, and this will be the height of the child collapsible which are still closed.
How can I make the maxHeight of the collapsibles dynamically change as their children collapsible expand?
You should check and modify max-height for elements on child opening and closing (partial code).
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function() {
this.classList.toggle("active");
var content = this.nextElementSibling;
if (content.style.maxHeight){
content.style.maxHeight = null;
} else {
content.style.maxHeight = content.scrollHeight + "px";
}
// from last to first to recalculate parent height after child
for (var j = coll.length - 1; j >= 0; j--) {
if (coll[j].classList.contains('active')) {
console.log(j, coll[j].classList);
var c2 = coll[j].nextElementSibling;
console.log(c2);
c2.style.maxHeight = null;
c2.style.maxHeight = c2.scrollHeight + "px";
}
}
});
}
But code above will work if css transition is disabled (because js triggers immediately before transition completed).
So, if transition is necessary you can add timeout to check for modification after transition ends. Something like this:
var coll = document.getElementsByClassName("collapsible");
var i;
var checkCollapsible = function() {
for (var j = coll.length - 1; j >= 0; j--) {
if (coll[j].classList.contains('active')) {
console.log(j, coll[j].classList);
var c2 = coll[j].nextElementSibling;
console.log(c2);
c2.style.maxHeight = null;
c2.style.maxHeight = c2.scrollHeight + "px";
}
}
};
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function() {
this.classList.toggle("active");
var content = this.nextElementSibling;
if (content.style.maxHeight){
content.style.maxHeight = null;
} else {
content.style.maxHeight = content.scrollHeight + "px";
}
window.setTimeout(checkCollapsible, 200);
});
}
https://jsfiddle.net/6yn0mewz/3/
There are also bunch of transitionend events available. So for webkit you may use something like (partial code)
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function() {
this.classList.toggle("active");
var content = this.nextElementSibling;
if (content.style.maxHeight){
content.style.maxHeight = null;
} else {
content.style.maxHeight = content.scrollHeight + "px";
}
content.addEventListener('webkitTransitionEnd', checkCollapsible);
});
}
https://jsfiddle.net/6yn0mewz/4/
See CSS3 transition events
and TransitionEnd Event not firing? for examples.
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
I am building a real-time HTML highlighter so that when a user selects a range of text that text is surrounded with span elements that have a background property.
Here is the fiddle: https://jsfiddle.net/4hd2vrex/
Problem is that this can get quite messy when users do multiple selects, the spans get nested and I get content like this:
<span style="background-color: rgb(255, 255, 131);">
r
<span style="background-color: rgb(255, 255, 131);">
<span style="background-color: rgb(255, 255, 131);">
e
</span>
p
</span>
r
<span style="background-color: rgb(255, 255, 131);">
e
</span>
h
<span style="background-color: rgb(255, 255, 131);">
end
</span>
e
<span style="background-color: rgb(255, 255, 131);">
rit
</span>
</span>
Holy Jackpot Batman! To remedy this I have the following idea:
Before adding any spans just replace all the selected text, span tags and all, with the original selected text window.getSelection().
So for example, if I selected that mess of spans above, before wrapping my selected text with more spans I would replace those spans with window.getSelection() which is just the text reprehenderit and I would get.
<span style="background-color: rgb(255, 255, 131);">reprehenderit</span>
Q:
How do I replace my selection with the selected text?
I have done the whole highlight text with my ways,not use window.Selection API,but use:select(start,end).then(merge).then(filter).then(highlight).and the most interesting things is that it can be highlight complex element,not even if text only.I found that the select api can also write a wysiwyg html editor,so I shared it to everyone who interesting on selection problem,and wish to help you,good question!
(function (context, factory) {
if (typeof module != 'undefined' && typeof module.exports == 'object') {
module.exports = factory(context);
} else {
factory(context, true);
}
})(window || this, function (context, bind) {
function promise(executor) {
return new Promise(executor);
}
var $TYPE = 'nodeType', $TEXT = 'textContent', $PARENT = 'parentNode', $NEXT = 'nextSibling', $FIRST = 'firstChild', NIL = {};
function leaf(node) {
return node[$TYPE] == 3;
}
function next(node, tree) {
var it = tree ? node[$FIRST] || node[$NEXT] : node[$NEXT];
if (it) {
if (leaf(it)) return it;
return next(it, true);
}
var parent = node[$PARENT];
return parent && next(parent);
}
function parent(node) {
return node[$PARENT];
}
function wrap(node, start, end) {
if (!node) throw 'node is null';
if (!leaf(node)) throw 'node is not a leaf:' + node.tagName;
var rawText = node[$TEXT];
var rawLength = rawText.length;
var self = {
node: node,
text: function (text) {
if (text !== undefined) {
node.textContent = text;
return wrap(node, 0, text.length);
}
return rawText.substring(self.start(), self.end());
},
is: function (other) {
return node == other.node;
},
start: function () {
return start === NIL || !start ? 0 : start;
},
end: function () {
return end === NIL || !end ? rawLength : end;
},
length: function () {
return self.end() - self.start();
},
to: function (end) {
return wrap(node, self.start(), end.end());
},
toLast: function () {
return wrap(node, start, rawLength);
},
next: function () {
var it = next(node);
return it && wrap(it);
},
split: function () {
if (self.length() >= rawLength) return self;
var stack = [0].concat(self.start() || []).concat(self.end()).concat(self.end() != rawLength ? rawLength : []);
var start = stack.shift();
var separated = [];
while (stack.length) {
var end = stack.shift();
var text = document.createTextNode(rawText.substring(start, end));
self.after(text);
separated.push(wrap(text));
start = end;
}
self.remove();
return !self.start() ? separated[0] : separated[1];
},
remove: function (optimized) {
var parent = node[$PARENT];
if (optimized && parent.childNodes.length == 1) {
parent[$PARENT].removeChild(parent);
}
parent.removeChild(node);
return this;
},
merge: function (other) {
var it = self.split();
return it.text(other.split().remove(true).text() + it.text());
},
after: function (e) {
node[$PARENT].insertBefore(e, node);
return this;
},
wrap: function (e) {
e.appendChild(self.split().after(e).node);
}
};
return self;
}
function select(start, end) {
return promise(function (resolve) {
start = wrap(start.text, start.offset, NIL), end = wrap(end.text, NIL, end.offset);
var selected = [];
while (start) {
if (start.is(end)) {
selected.push(start.to(end));
break;
}
selected.push(start.toLast());
start = start.next();
}
resolve(selected);
});
}
function merge(filter) {
return function (parts) {
var result = [parts.shift()];
while (parts.length) {
var prev = result.pop();
var next = parts.shift();
if (filter(prev.node, next.node)) {
result.push(next.merge(prev));
} else {
result.push(prev);
result.push(next);
}
}
return result;
}
}
function filter(test) {
return function (parts) {
return parts.filter(function (part) {
return test(part.node);
});
}
}
function apply(consume) {
return function (parts) {
return parts.forEach(function (part) {
return consume(part);
});
}
}
var exports = {
__esModule: true,
default: select,
select: select,
merge: merge,
filter: filter,
apply: apply
};
if (bind)for (var name in exports)context[name] = exports[name];
return exports;
});
(function () {
var COMPONENT_ID = 'highlight-' + +new Date;
var highlighter = {
init: function () {
this.bindEvents();
},
/**
*
*/
bindEvents: function () {
var self = this;
$('.swatch').on('click', function () {
$('.swatch').removeClass('active');
$(this).addClass('active');
});
$('.content').mouseup(function () {
var current = self.actived();
if (current.hasClass('clear')) {
self.clear();
} else {
self.highlight();
}
});
},
actived: function () {
return $('.swatch.active');
},
color: function () {
return this.actived().css('background-color');
},
/**
*
*/
highlight: function () {
var self = this;
var selection = self.getSelection();
if (selection) {
self.select(selection.getRangeAt(0)).//
then(merge(function (left, right) {
var p1 = left.parentNode;
var p2 = right.parentNode;
var a1 = self.compare(left);
var a2 = self.compare(right);
return (a1 && a2 && p1.parentNode == p2.parentNode) ||
(!a1 && !a2 && p1 == p2) ||
(a1 && !a2 && p1.parentNode == p2) ||
(!a1 && a2 && p2.parentNode == p1);
})).then(filter(function (part) {
return !self.compare(part);
})).then(function (parts) {
parts.map(function (node) {
node.wrap(self.component());
});
}).catch(function (e) {
console.log(e);
});
selection.removeAllRanges();
}
},
component: function () {
return $('<span data-toggle="' + COMPONENT_ID + '">').css('background-color', this.color()).get(0);
},
compare: function (text) {
var self = this;
var parent = $(text).parent();
var highlighted = parent.is(self.selector());
var color = parent.css('background-color');
return highlighted && color == self.color();
},
selector: function () {
return '[data-toggle="?"]'.replace(/\?/, COMPONENT_ID);
},
clear: function () {
var self = this;
var selection = self.getSelection();
if (selection) {
self.select(selection.getRangeAt(0)).then(apply(function (part) {
var text = $(part.split().node);
while (true) {
var comp = text.closest(self.selector());
if (!comp || !comp.length) {
break;
}
var children = comp.contents();
var first = children[0], last = children[children.length - 1];
if (text.is(last)) {
comp.after(text);
} else if (text.is(first)) {
comp.before(text);
} else {
var heading = comp.clone().empty();
for (var i = 0; i < children.length; i++) {
if (text.is(children[i])) {
break;
}
heading.append(children[i]);
}
comp.before(heading).before(text);
}
if (first == last) comp.remove();
}
}));
selection.removeAllRanges();
}
},
select: function (range) {
return select(
{text: range.startContainer, offset: range.startOffset},
{text: range.endContainer, offset: range.endOffset}
);
},
getSelection: function () {
var sel = window.getSelection();
return /^\s*$/.test(self && sel.toString()) ? null : sel;
}
};
highlighter.init();
})();
body {
margin: 0;
background: #fafafa;
box-shadow: 0 0 5rem rgba(0, 0, 0, 0.25) inset;
}
::-moz-selection {
background-color: rgba(0, 0, 0, 0.2);
}
::selection {
background-color: rgba(0, 0, 0, 0.2);
}
.content {
padding: 100px;
}
.footer {
padding: 0 100px 0 100px;
flex-basis: 100%;
height: 60px;
background: #292B2C;
position:fixed;top:0;width:100%;
}
.footer .items-left {
float: left;
}
.footer-item {
line-height: 60px;
}
#colors {
padding: 12px;
}
.swatch {
width: 30px;
height: 30px;
border-radius: 15px;
box-shadow: inset 0px 1px 0px rgba(255, 255, 255, 0.5), 0px 2px 2px rgba(0, 0, 0, 0.5);
display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="content">
<span style="color:red;"><b>Content</b> <i>Lorem</i> <font size='7'>ipsum</font> dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim</span> veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
laborum.
Content Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
laborum.
Content Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
laborum.
Content Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
laborum.
</div>
<div class="footer">
<div class="items-left">
<div id="colors">
<div class="swatch active" style="background-color: rgba(255,255,131,.5);"></div>
<div class="swatch" style="background-color: rgba(255,140,218,.5);"></div>
<div class="swatch" style="background-color: rgba(144,255,184,.5);"></div>
<div class="swatch clear"></div>
</div>
</div>
</div>
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/