JS detect when an html element wraps down - javascript

I would like to scroll down to an element when it is created in a div, or when the element wraps down so you can always see the entire text since the text is added slowly to the element.
I have already got a way to auto-scroll to elements when they are created using element.scrollIntoView().
this.setText = function (text) {
this.text = text
this.progress = move
this.element.innerHTML = '[Player]>'
this.element.scrollIntoView({behavior: "smooth"})
}
this.run = function () {
if (this.progress < this.text.length) {
// Add animation for different speeds of typing keys
this.progress += 0.8 * Math.random()
} else {
this.progress = this.text.length
}
// Draw the text at the current line number
const currLine = Math.floor(clamp(this.progress, 0, this.text.length))
this.element.innerHTML = '[Player]>' + this.text.slice(0, currLine)
}
That is the way I add text to the element slowly. I would like to detect the wrap instead of repeatedly scrolling into view the element
Edit: I would have liked to detect when the element wraps not see if it has wrapped i.e. something like window.onElementWrap() but I can work with what you have given me.
The way I can solve my issue is each loop checking if the height is different from the stored height, and if it is, scrolling it into view as well as setting the stored height to the current height.

You can check the element's offsetHeight (eg: this.element.offsetHeight), it will change when the text wraps to a new line.
Here is an example:
const text = `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Interdum varius sit amet mattis vulputate enim nulla. Est ullamcorper eget nulla facilisi etiam dignissim diam quis enim. Pharetra pharetra massa massa ultricies mi. In egestas erat imperdiet sed euismod nisi porta lorem mollis. Nunc mi ipsum faucibus vitae aliquet nec ullamcorper. Aliquam sem et tortor consequat id porta nibh venenatis cras. Aliquam sem et tortor consequat id porta nibh venenatis. Lectus quam id leo in. Pharetra vel turpis nunc eget lorem dolor. Euismod lacinia at quis risus sed vulputate odio. Tempus iaculis urna id volutpat lacus laoreet non. Mattis rhoncus urna neque viverra justo. Massa eget egestas purus viverra. Gravida dictum fusce ut placerat orci. In massa tempor nec feugiat. Mauris cursus mattis molestie a. Ultrices in iaculis nunc sed augue lacus viverra. Vel risus commodo viverra maecenas accumsan. Lectus nulla at volutpat diam ut venenatis tellus. Vestibulum mattis ullamcorper velit sed ullamcorper. Fermentum et sollicitudin ac orci phasellus. Justo laoreet sit amet cursus sit amet dictum. Ipsum consequat nisl vel pretium lectus quam id. Et ligula ullamcorper malesuada proin libero nunc consequat interdum. Pulvinar proin gravida hendrerit lectus. Aliquet nibh praesent tristique magna sit.`
let lines = text.match(/.{150}/g);
lines = [...lines, ...lines];
lines = [...lines, ...lines];
let index = 0;
let prevHeight = testElement.offsetHeight;
const interval = setInterval(() => {
testElement.textContent += lines[index];
const currentHeight = testElement.offsetHeight;
if (currentHeight > prevHeight) {
window.scroll(window.scrollX, window.scrollY + (currentHeight - prevHeight));
}
prevHeight = currentHeight;
index++;
if (index >= lines.length) {
clearInterval(interval);
}
}, 100);
<div id="testElement"></div>

Related

JavaScript - scrollHeight with rows attribute

I want to get the height of the text inside a textarea. Here is the problem: the textarea has a fixed number of rows, causing the scrollHeight property to become useless in this case. See this example:
console.log(document.querySelector('textarea').scrollHeight);
<textarea rows="10">Hello, world!</textarea>
My idea was to remove the rows attribute with JavaScript, retrieve the scrollHeight, and reapply the attribute.
However, this causes jumps on the page in Safari, so it is not a solution.
Does anyone know of a property/function that return this value? Thanks in advance!
To be "more or less" bullet proof, solution has to consider the real computed size of your text.
const scrollOrNot = (el, nbline) => {
const divTest = document.querySelector('#test');
divTest.style.fontsize = el.style.fontsize;
divTest.style.lineHeight = el.style.lineHeight;
divTest.style.width = el.getBoundingClientRect().width + 'px';
divTest.innerHTML = 'W';
const h_one_line = divTest.getBoundingClientRect().height;
divTest.innerHTML = el.value;
if (Math.floor((divTest.getBoundingClientRect().height / h_one_line)) > nbline) {
console.log((divTest.getBoundingClientRect().height / h_one_line));
el.setAttribute('rows', 10);
}
}
scrollOrNot(document.querySelector('#textarea1'),10);
scrollOrNot(document.querySelector('#textarea2'),10);
#test {
position: absolute;
top: -3000px;
height: auto;
}
<textarea id="textarea1">Hello, world!</textarea>
<textarea id="textarea2">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. In egestas erat imperdiet sed euismod nisi porta lorem mollis. Turpis massa tincidunt dui ut ornare lectus. Pellentesque dignissim enim sit amet venenatis urna. In fermentum posuere urna nec tincidunt. Venenatis urna cursus eget nunc scelerisque viverra mauris in. Non quam lacus suspendisse faucibus interdum posuere lorem ipsum. Nunc id cursus metus aliquam eleifend mi in nulla. Netus et malesuada fames ac turpis egestas integer eget. Sit amet dictum sit amet justo donec enim diam. Aliquam purus sit amet luctus venenatis lectus. Ligula ullamcorper malesuada proin libero nunc. Venenatis lectus magna fringilla urna porttitor rhoncus. Faucibus interdum posuere lorem ipsum dolor. Posuere ac ut consequat semper viverra nam libero justo laoreet. Ac turpis egestas sed tempus urna et pharetra pharetra massa. Vestibulum sed arcu non odio euismod lacinia. Arcu non odio euismod lacinia at quis risus sed. Lobortis mattis aliquam faucibus purus in massa tempor nec. Enim ut sem viverra aliquet eget sit amet tellus cras.
</textarea>
<div id="test"></div>
here you have a div at -3000px, absolute.
you put same width, fontsize and line height of your textarea.
You put a 'W' in it (biggest letter for latin character)
you take it's height
you put your text in
you take the new height
if division more than requested lines...

How to make the chrome browser window move down to show text continuously without user input?

This page is for an art project; it should look like a ghost is typing continuously. More "real" text will be added and I need it to loop back at the end. The #1 problem is when the text hits bottom of page it continues, but can't be seen without user scrolling.
My cobbled together code is kind of working in chrome; however, I want the page to automatically move down when the copy continues to appear line by line at the bottom of the page, (without input from user). Also, I'm unable to break the lines with <br> or <p>. Tried using back tics instead of quotes and single quotes instead of double; line breaks don't work.
Below is the code I'm using:
<!DOCTYPE html>
<html>
<head>
<style>
#font-face {
font-family: MyUnderwood;
src: url(~/typeWriter/MyUnderwood.ttf);
}
body {
font-family: MyUnderwood;
font-size: 30px;
margin: 10px 50px;
letter-spacing: 6px;
font-weight: normal;
</style>
</head>
<body onload="typeWriter()">
<div id="testType"></div>
<script>
var i = 0;
var speed = 100;
function typeWriter() {
if (i < txt.length) {
document.getElementById("testType").textContent += txt.charAt(i);
i++;
setTimeout(typeWriter, speed);
}
}
var txt = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Sed faucibus turpis in eu mi. Tortor vitae purus faucibus ornare suspendisse sed nisi. Elementum curabitur vitae nunc sed velit. Accumsan in nisl nisi scelerisque eu ultrices vitae auctor. Lectus sit amet est placerat in egestas erat imperdiet sed. In iaculis nunc sed augue lacus. Tristique nulla aliquet enim tortor at. Ac placerat vestibulum lectus mauris ultrices. Fermentum leo vel orci porta non pulvinar neque. Erat imperdiet sed euismod nisi. Vel fringilla est ullamcorper eget nulla facilisi etiam. Eget nullam non nisi est. A diam maecenas sed enim. Pulvinar etiam non quam lacus suspendisse faucibus interdum. Nullam eget felis eget nunc lobortis. Urna cursus eget nunc scelerisque viverra mauris in. Ultrices tincidunt arcu non sodales neque sodales ut etiam sit. Vitae sapien pellentesque habitant morbi tristique senectus et netus et. Purus non enim praesent elementum facilisis leo vel fringilla est. Aliquet sagittis id consectetur purus. Venenatis lectus magna fringilla urna porttitor rhoncus dolor. Arcu odio ut sem nulla pharetra diam sit amet."
;
document.addEventListener("DOMContentLoaded", typeWriter);
</script>
</body>
</html>
All you need is to force the scrolling parent (in this case it will be document.documentElement to scroll by its scrollable height. Technically speaking the scrollable height is scrollHeight - offsetHeight, but the scrollTo function is smart enough that if you provide a value that is too large it will just scroll to the bottom of the element.
Then it is just a matter of calling document.documentElement.scrollTo(0, document.documentElement.scrollHeight). See proof-of-concept below (I have increased the speed by a factor of 10).
A usability warning tho: by constantly updating the scroll position can cause frustration to the user as it will overwrite whatever scroll position they are at: e.g. if a user scrolls up to read the earlier text they will be forcibly scrolled to the bottom every time a new character is inserted.
let i = 0;
const speed = 10;
function typeWriter() {
if (i < txt.length) {
document.getElementById("testType").textContent += txt.charAt(i);
i++;
setTimeout(typeWriter, speed);
document.documentElement.scrollTo(0, document.documentElement.scrollHeight);
}
}
const txt = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Sed faucibus turpis in eu mi. Tortor vitae purus faucibus ornare suspendisse sed nisi. Elementum curabitur vitae nunc sed velit. Accumsan in nisl nisi scelerisque eu ultrices vitae auctor. Lectus sit amet est placerat in egestas erat imperdiet sed. In iaculis nunc sed augue lacus. Tristique nulla aliquet enim tortor at. Ac placerat vestibulum lectus mauris ultrices. Fermentum leo vel orci porta non pulvinar neque. Erat imperdiet sed euismod nisi. Vel fringilla est ullamcorper eget nulla facilisi etiam. Eget nullam non nisi est. A diam maecenas sed enim. Pulvinar etiam non quam lacus suspendisse faucibus interdum. Nullam eget felis eget nunc lobortis. Urna cursus eget nunc scelerisque viverra mauris in. Ultrices tincidunt arcu non sodales neque sodales ut etiam sit. Vitae sapien pellentesque habitant morbi tristique senectus et netus et. Purus non enim praesent elementum facilisis leo vel fringilla est. Aliquet sagittis id consectetur purus. Venenatis lectus magna fringilla urna porttitor rhoncus dolor. Arcu odio ut sem nulla pharetra diam sit amet.";
document.addEventListener("DOMContentLoaded", typeWriter);
body {
font-family: monospace;
font-size: 30px;
margin: 10px 50px;
letter-spacing: 6px;
font-weight: normal;
}
<div id="testType"></div>
We can do better with async/await
There is actually no need to keep track of the index i, if we use a for...of loop. Moreover, if we use async/await we can make things a lot more readable without needing to worry about recursion, when used with a for loop:
const speed = 10;
async function sleep(d) {
return new Promise(r => setTimeout(r, d));
}
const container = document.getElementById('testType');
async function typeWriter() {
for (const character of txt) {
container.textContent += character;
document.documentElement.scrollTo(0, document.documentElement.scrollHeight);
await sleep(speed);
}
}
const txt = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Sed faucibus turpis in eu mi. Tortor vitae purus faucibus ornare suspendisse sed nisi. Elementum curabitur vitae nunc sed velit. Accumsan in nisl nisi scelerisque eu ultrices vitae auctor. Lectus sit amet est placerat in egestas erat imperdiet sed. In iaculis nunc sed augue lacus. Tristique nulla aliquet enim tortor at. Ac placerat vestibulum lectus mauris ultrices. Fermentum leo vel orci porta non pulvinar neque. Erat imperdiet sed euismod nisi. Vel fringilla est ullamcorper eget nulla facilisi etiam. Eget nullam non nisi est. A diam maecenas sed enim. Pulvinar etiam non quam lacus suspendisse faucibus interdum. Nullam eget felis eget nunc lobortis. Urna cursus eget nunc scelerisque viverra mauris in. Ultrices tincidunt arcu non sodales neque sodales ut etiam sit. Vitae sapien pellentesque habitant morbi tristique senectus et netus et. Purus non enim praesent elementum facilisis leo vel fringilla est. Aliquet sagittis id consectetur purus. Venenatis lectus magna fringilla urna porttitor rhoncus dolor. Arcu odio ut sem nulla pharetra diam sit amet.";
document.addEventListener("DOMContentLoaded", typeWriter);
body {
font-family: monospace;
font-size: 30px;
margin: 10px 50px;
letter-spacing: 6px;
font-weight: normal;
}
<div id="testType"></div>
Want to use line-breaks?
If you want to introduce line breaks, I would suggest you split it up into an array of texts instead. The reason being that you still want to treat your string "as-is", but need to arbitrarily insert HTML <br /> tags to create spacing.
You can store this info in any way you want, but let's say we run with an array of objects that basically store a string and an optional "breakAfter", which will insert x number of <br /> tags as you see fit:
const speed = 10;
const container = document.getElementById('testType');
async function sleep(d) {
return new Promise(r => setTimeout(r, d));
}
async function typeWriter() {
for (const entry of texts) {
// Assume we want to start a new line with every entry
// When `entry.breakAfter` is undefined we fallback to 1
// NOTE: This is ES6 object destructuring!
const { text, breakAfter = 1 } = entry;
for (const character of text) {
container.innerHTML += character;
await sleep(speed);
}
for (let j = 0; j < breakAfter ; j++) {
container.innerHTML += '<br />';
await sleep(speed);
}
}
}
const texts = [{
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit",
},
{
text: "Foo",
breakAfter: 3,
},
{
text: "Bar",
breakAfter: 5,
}, {
text: "End"
}
];
document.addEventListener("DOMContentLoaded", typeWriter);
body {
font-family: monospace;
font-size: 30px;
margin: 10px 50px;
letter-spacing: 6px;
font-weight: normal;
}
<div id="testType"></div>

How to count chars in a CSS limited div with JS

I have the following CSS:
html {
--lh: 18px;
}
.line-10 {
--max-lines: 10;
max-width: 100%;
max-height: calc(var(--lh) * var(--max-lines));
overflow: hidden;
}
and want to count chars in a div:
const div = document.createElement('div')
div.classList.add('line-10')
div.innerHTML = longHtml
const _createdHTML = div.innerHTML
const _createdTxt = _createdHTML.replace(/<[^>]*>/g, '')
console.log('_createdTxt', _createdTxt.length)
but I am getting the entire longHtml instead of limited by CSS. Any way to solve that?
Current solution for case A (1 big paragraph of text per div):
const maxLines = 10
const fullTxtLen = html.replace(/<[^>]*>/g, '').length
const fullLineLen = 150 // or whatever fits into initial div
if (fullTxtLen / fullLineLen > maxLines) {
const totalLines = fullTxtLen / fullLineLen
const reduceIn = totalLines / maxLines
const final = fixHtml(html.substring(0, parseInt(html.length / reduceIn)))
return {
html: final,
len: final.replace(/<[^>]*>/g, '').length // i.e. this is the answer on visible char count
}
}
function fixHtml (html) {
const div = document.createElement('div')
div.innerHTML = html
return (div.innerHTML)
}
Problems with this solution:
than introducing more <p> and/ or <br />, it will make it pretty unreliable due to different fullLineLen for each line that does not end that the div right border.
reduced char length is in text characters, not html characters
FAQ
how many characters you need to keep, input examples and expected outputs
It depends on the size of the div. Input examples: 1) lorem ipsum in one paragraph, 2) lorem ipsum broken into different paragraphs. Expected output - visible char count.
If we copy characters from the string gradually into a div that is offscreen with the same max-height (+1px) as div, intersectionObserver will let us know when it just enters the top of the screen. This will be when the first characters from the line after the last visible line are copied.
We have to let the system have a space in which it can tell us of such an observation so the copying has to be done in bits. This snippet copies a word at a time so on most systems it probably can count at a maximum of 60 words per second.
There are some uncertainties - what do we mean by a character being visible and what does the system mean? On a system in which there are accurately 10 lines in the visible portion and without scrolling the counting is accurate (concurs with e.g. Word or Notepad++).
On scrolling it is quite possible that just part of the characters in the top line and in the bottom line are shown (sometimes not really perceptibly), so what the system and what a human thinks is visible and should be counted may differ. It is not clear from the question what the expected result should be. This snippet does its best in the scrolled situation by counting characters that have been scrolled out at the top to adjust the overall count.
Tested on Edge/Chrome on Windows 10 and Safari on IOS 14.4. Note that on Chrome's dev tools emulators it does not work well (counts a few too many characters) as the emulation of intersectionObserver seems to be a bit laggy.
<head>
<style>
html {
--lh: 18px;
}
* {
margin: 0;
}
.line-10, .workSpaceDiv {
margin: 0 20px;
--max-lines: 10;
max-width: 100%;
max-height: calc(var(--lh) * var(--max-lines));
overflow-y: scroll;
}
.workSpaceDiv {
position: absolute;
--maxh: calc(var(--lh) * (var(--max-lines) + 1));
max-height: var(--maxh);
transform: translateY(calc(2px - var(--maxh)));
height: auto;
display: none;
}
.clickme, .info {
padding: 10px;
}
.infoblock {
padding: 20px;
}
</style>
</head>
<body>
<div class="workSpaceDiv"></div>
<div class="infoblock">
<button class="clickme" onclick="countChs(div);">Click me to count the visible characters</button>
<span class="info"> Number of visible characters: </span><span class="count"></span>
</div>
<script>
const div = document.createElement('div');
div.classList.add('line-10');
document.body.appendChild(div);
const longHtml = `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Mi in nulla posuere sollicitudin. Sed nisi lacus sed viverra tellus in. Sit amet consectetur adipiscing elit pellentesque habitant morbi tristique. Enim lobortis scelerisque fermentum dui faucibus. Varius duis at consectetur lorem donec massa sapien faucibus et. Libero enim sed faucibus turpis in eu mi bibendum. Eleifend donec pretium vulputate sapien nec sagittis aliquam malesuada bibendum. Nulla aliquet enim tortor at auctor. Mattis pellentesque id nibh tortor. Ullamcorper sit amet risus nullam eget felis. In ornare quam viverra orci sagittis eu. Pellentesque habitant morbi tristique senectus. Amet cursus sit amet dictum sit amet justo. Sit amet nulla facilisi morbi tempus. Dolor sit amet consectetur adipiscing elit duis tristique sollicitudin nibh. Facilisi morbi tempus iaculis urna id volutpat lacus laoreet. Mi in nulla posuere sollicitudin aliquam ultrices. Diam quis enim lobortis scelerisque fermentum dui faucibus in ornare. Sed augue lacus viverra vitae congue eu. Et malesuada fames ac turpis egestas integer eget aliquet nibh. Ipsum faucibus vitae aliquet nec ullamcorper. Felis bibendum ut tristique et egestas quis ipsum suspendisse ultrices. Ac felis donec et odio pellentesque diam volutpat commodo. Mauris a diam maecenas sed. Facilisis sed odio morbi quis commodo odio aenean sed. Lorem mollis aliquam ut porttitor leo a. Vivamus at augue eget arcu dictum varius duis. Nisi porta lorem mollis aliquam ut. Habitant morbi tristique senectus et netus et malesuada fames ac. Tempus iaculis urna id volutpat lacus laoreet non curabitur. Sed risus pretium quam vulputate dignissim suspendisse in. Malesuada fames ac turpis egestas maecenas pharetra. Malesuada fames ac turpis egestas maecenas. Urna nunc id cursus metus aliquam eleifend mi in. Convallis posuere morbi leo urna molestie at elementum. Facilisis leo vel fringilla est ullamcorper eget nulla facilisi. Nulla pharetra diam sit amet nisl suscipit. Posuere morbi leo urna molestie at. Risus pretium quam vulputate dignissim. Arcu dui vivamus arcu felis bibendum ut tristique et. Varius duis at consectetur lorem donec massa sapien faucibus et. Id eu nisl nunc mi ipsum faucibus vitae aliquet. Congue mauris rhoncus aenean vel elit scelerisque mauris. Nulla at volutpat diam ut venenatis tellus in. Tellus cras adipiscing enim eu turpis egestas pretium aenean pharetra. Leo integer malesuada nunc vel risus. Tortor at auctor urna nunc id cursus metus aliquam eleifend. Felis bibendum ut tristique et egestas quis ipsum. A condimentum vitae sapien pellentesque habitant morbi. Purus non enim praesent elementum facilisis leo vel fringilla. Sagittis purus sit amet volutpat consequat mauris nunc. Sed tempus urna et pharetra pharetra massa massa. Vitae proin sagittis nisl rhoncus mattis rhoncus. Non curabitur gravida arcu ac tortor dignissim convallis. Dolor sit amet consectetur adipiscing elit. Dignissim enim sit amet venenatis urna cursus. Neque ornare aenean euismod elementum nisi quis eleifend quam. Tortor at auctor urna nunc id cursus metus aliquam eleifend. Curabitur gravida arcu ac tortor dignissim convallis aenean. Neque viverra justo nec ultrices dui sapien eget mi.`;
const createdTxt = longHtml.replace(/<[^>]*>/g, '');
div.innerHTML = createdTxt;
const workSpaceDiv = document.querySelector('.workSpaceDiv');
const info = document.querySelector('.info');
const count = document.querySelector('.count');
let nextCh, lastWordSize, stopCount, scrolledChs, firstTime, scrolled, allChs;
function countChsIn() {
stopCount = false;
nextCh = 0;
lastWordSize = 0;
observer.observe(workSpaceDiv);
requestAnimationFrame(nextChFill);
function nextChFill() {
if (stopCount) return;
lastWordSize = 0;
let i, ch;
for (i = 0; i < 10; i++) {
ch = allChs.charAt(nextCh);
workSpaceDiv.innerHTML = workSpaceDiv.innerHTML + ch;
nextCh++;
lastWordSize++;
if ((!((/[a-zA-Z]/).test(ch))) || (nextCh >= allChs.length)) { break; }
}
if (nextCh < allChs.length) requestAnimationFrame(nextChFill);
else { finish(); }
}
}
function finish() {
observer.disconnect(workSpaceDiv);
workSpaceDiv.style.height = 'auto';
workSpaceDiv.innerHTML = '';
if (firstTime) {
firstTime = false;
stopCount = false;
scrolledChs = nextCh - lastWordSize -1;
workSpaceDiv.style.transform = 'translateY(calc(1px - (var(--maxh) + ' + scrolled + 'px' + ')))';
workSpaceDiv.style.maxHeight = 'calc(var(--maxh) + ' + scrolled + 'px' + ')';
requestAnimationFrame(countChsIn);
}
else {
workSpaceDiv.style.transform = 'translateY(calc(2px - var(--maxh)))';
workSpaceDiv.style.display = 'none';
count.innerHTML = nextCh - lastWordSize - 1 - scrolledChs;
stopCount = true;
}
}
function overlap(entries) {
entries.forEach(entry => {
if (entry.isIntersecting) {
finish();
}
});
}
let observer = new IntersectionObserver(overlap);
function countChs(el) {
allChs = el.innerHTML;
scrolled = el.scrollTop;
firstTime = !(scrolled <= 0);
count.innerHTML = '...counting...';
workSpaceDiv.style.display = 'block';
workSpaceDiv.style.innerHTML = '';
workSpaceDiv.style.height = 'auto';
if (firstTime) {
workSpaceDiv.style.transform = 'translateY(calc(2px - ' + scrolled + 'px' + '))';
workSpaceDiv.style.maxHeight = scrolled + 'px';
}
else {
workSpaceDiv.style.transform = 'translateY(calc(2px - var(--maxh)))';
workSpaceDiv.style.maxHeight = 'var(--maxh)';
}
scrolledChs = 0;
requestAnimationFrame(countChsIn);
}
</script>
</body>

HTML5 Touch event: Getting word touched

I'm building a dicionary function for my website for mobile users.
I would like this function:
The user touches on a word, a popup window shows its definition.
I've found out the touch API of HTML5.
But couldn't figure out how to get the word touched.
Any hint?
You can sort of achieve it thanks to the Range API and its handy getBoundingClientRect method.
Indeed, this method allows us to get the position of a textNode, while normally we can only get the position of an Element.
All we have to do then, is to first retrieve the original textNode that got clicked, split its content into as many textNodes as there are words, and grab the one we want.
document.onclick = function(e){ console.log(getWord(e));};
// didn't really test with touch device yet, but should be about the same...
document.ontouchstart = function(e){ console.log(getWord(e.touches[0]));};
function getWord(e) {
// FF gives us a shortcut
var target = e.explicitOriginalTarget || e.target,
// We will use this to get the positions of our textNodes
range = document.createRange(),
rect, i;
// so first let's get the textNode that was clicked
if (target.nodeType !== 3) {
var children = target.childNodes;
i = 0;
while (i < children.length) {
range.selectNode(children[i]);
rect = range.getBoundingClientRect();
if (rect.left <= e.clientX && rect.right >= e.clientX &&
rect.top <= e.clientY && rect.bottom >= e.clientY) {
target = children[i];
break;
}
i++;
}
}
if (target.nodeType !== 3) {
return '[not a textNode]';
}
// Now, let's split its content to words
var words = target.nodeValue.split(' '),
textNode, newText;
i = 0;
while (i < words.length) {
// create a new textNode with only this word
textNode = document.createTextNode((i ? ' ' : '') + words[i]);
newText = words.slice(i + 1);
// update the original node's text
target.nodeValue = newText.length ? (' ' + newText.join(' ')) : '';
// insert our new textNode
target.parentNode.insertBefore(textNode, target);
// get its position
range.selectNode(textNode);
rect = range.getBoundingClientRect();
// if it is the one
if (rect.left <= e.clientX && rect.right >= e.clientX &&
rect.top <= e.clientY && rect.bottom >= e.clientY) {
return words[i];
}
i++;
}
};
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ac fermentum ipsum, in efficitur felis. Nam fringilla semper lectus, pretium luctus tortor. Pellentesque vel aliquet orci. Quisque at accumsan felis. Phasellus a ligula congue, viverra tellus
sit amet, consequat arcu. Mauris et congue diam, eget vehicula ante. Curabitur varius augue eget augue posuere, ut tincidunt purus sodales. Suspendisse luctus fermentum justo, sed scelerisque ipsum dignissim nec.
</p>
<p>
Suspendisse sed dui elit. Vivamus laoreet ipsum ut metus finibus, eu ultrices lorem bibendum. Fusce ante nibh, egestas nec neque id, semper volutpat orci. Donec a porta nunc. Sed interdum hendrerit mauris. Donec sed semper eros. Integer rhoncus diam quis
augue ornare consequat. Sed eleifend congue ante quis malesuada. Maecenas et purus venenatis arcu malesuada condimentum id quis nunc. Integer non suscipit sapien, id iaculis mi.
</p>
<p>
Mauris cursus mauris vitae nulla tempor lacinia. Quisque tristique ullamcorper magna et consequat. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Phasellus faucibus sit amet enim non sollicitudin. Aliquam malesuada,
massa hendrerit condimentum pulvinar, ex est finibus massa, at efficitur velit est eu dolor. Pellentesque facilisis ligula hendrerit lacus efficitur venenatis. Ut pretium turpis quis laoreet mattis. Quisque tempor ornare maximus. Nunc augue urna, egestas
non lectus at, fringilla pharetra nisl. Pellentesque ac quam cursus, volutpat libero eget, ultricies dolor. Donec eleifend iaculis dui dignissim pulvinar. Cras semper vehicula augue, ut ultricies arcu consectetur ut. Mauris congue turpis justo, ac feugiat
dolor semper nec. Donec id augue posuere, efficitur nunc in, hendrerit est. Morbi eget pretium odio.
</p>
<p>
Morbi at volutpat nulla. Pellentesque elementum blandit imperdiet. Morbi id lacinia quam. Sed sed accumsan mauris. Donec nulla mi, rutrum id blandit id, hendrerit at turpis. Integer ante orci, venenatis a felis id, suscipit dignissim metus. Pellentesque
dictum, lectus aliquam facilisis mattis, nisi est porttitor nulla, et semper enim ante quis ex. Quisque ante magna, viverra et ante quis, fringilla tempor mauris. Phasellus posuere nulla in eleifend egestas. Proin augue arcu, semper quis metus sit amet,
placerat porta lorem. Integer tempus mi at faucibus posuere. Vestibulum ut malesuada turpis. In malesuada eros nisl, eget laoreet velit auctor vitae.
</p>
<p>
Aliquam maximus varius metus vel congue. Ut tristique risus metus, in tempor leo tempor laoreet. Donec scelerisque ipsum vitae nisi facilisis eleifend. Vivamus condimentum risus non lectus bibendum elementum. Cras vitae malesuada lorem. Curabitur et posuere
massa, ut luctus mi. Sed et enim nec nisl sollicitudin semper. Donec eget consectetur leo, a faucibus nulla. Nam accumsan hendrerit neque, ultrices aliquam ex auctor id. Nunc euismod faucibus ullamcorper. Sed ornare interdum congue. Quisque eget erat
erat. Morbi congue orci vitae porttitor semper.
</p>
this will be a little bit tricky. if you want to know the specific word touched inside a sentence, you should save a word in a separate span, register a touch event on all spans and get the text content on touch.
Please see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener for event listeners and
https://developer.mozilla.org/en-US/docs/Web/API/Touch_events/Using_Touch_Events for capturing touch events.

jquery - trimming image to remove div

i am trying through jquery to remove a div completely when there is no child element within it. The variable I am using is an image. I can't seem to select the variable as an image, i only know how to do this through text. Could someone please help me and resolve this issue, so it detects that there is a img contained within the div and displays it and when there isn't an image it doesn't display it.
I have uploaded it through jsfiddle:http://jsfiddle.net/cLkFD/
<div class="content-container"><!--content-container-->
<h3>title</h3>
<div class="picture"><!--picture-->
<img src="images/buddha.jpg" width="981" height="324" alt=""/>
</div><!--/picture-->
<div class="content"><!--content-->
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas et cursus neque, sit amet blandit tellus. Cras leo mauris, laoreet quis consequat et, pharetra mattis risus. Duis tortor lorem, ultrices egestas arcu sed, pretium egestas est. Morbi condimentum sem quis tortor placerat iaculis. Ut nisl augue, rutrum sed sem et, blandit hendrerit nibh. Donec et rhoncus odio, id tempor enim. Praesent ultricies justo vulputate dui gravida fermentum. Pellentesque id aliquet leo. Quisque vulputate, dolor vel rutrum accumsan, turpis odio faucibus sem, vel malesuada eros turpis ac magna. Nullam fermentum vehicula odio, ut ornare justo varius nec. Morbi lectus leo, porttitor nec elementum at, suscipit aliquam nunc. Ut gravida a sem ut imperdiet. Duis et turpis eget magna lobortis accumsan. In ac tincidunt nibh, quis pulvinar risus.
</p>
<button class="btn btn-1 btn-1a">Read More</button>
</div><!--content-->
</div><!--content-container-->
(function($) {
$.fn.equalHeights = function(minHeight, maxHeight) {
tallest = (minHeight) ? minHeight : 0;
this.each(function() {
if($(this).height() > tallest) {
tallest = $(this).height();
}
});
if((maxHeight) && tallest > maxHeight) tallest = maxHeight;
return this.each(function() {
$(this).height(tallest).css("overflow","hidden");
});
}
})(jQuery);
You can check to see whether any image inside your div or not using length:
$(function () {
$('.picture').each(function () {
if ($(this).find('img').length) {
$(this).hide();
}
});
});
http://jsfiddle.net/9VBJP/
The above fiddle should work
$('.picture').each(function(){
var imageElem = $(this).find('img');
if(imageElem.length == 0) {
$(this).hide();
}
});

Categories

Resources