Transition on height using javascript style.height - javascript

In order to manipulate height property, so I use javascript not jquery then I got a problem.
The problem is that I cannot override the height back to zero once I have set to the scrollHeight of the element.
Here is my js code:
let isClosed = true;
var cals = document.getElementsByClassName('h-strench-box');
for (var i = 0; i < cals.length; i ++) {
cals[i].addEventListener('click', function(e) {
if (isClosed) {
this.classList.add('h-strench-box-out');
var content = this.querySelector('.h-strench-content');
content.style.height = content.scrollHeight + 'px';
isClosed = false;
} else {
if (this.classList.contains('h-strench-box-out')) {
this.classList.remove('h-strench-box-out');
// this.querySelector('.h-strench-content').style.height = '0';
// This not working
isClosed = true;
} else {
for (var j = 0; j < cals.length; j ++) {
cals[j].classList.remove('h-strench-box-out');
cals[j].querySelector('.h-strench-content').style.height = '0';
// This not working
}
this.classList.add('h-strench-box-out');
var content = this.querySelector('.h-strench-content');
content.style.height = content.scrollHeight + 'px';
}
}
});
}
css
.h-strench-content {
height: 0;
padding: 0;
display: none;
overflow: hidden;
transition: height 0.4s ease-in;
}
.h-strench-box-out .h-strench-content {
display: block;
}
.h-strench-btn {
transition: transform 0.3s ease-out;
}
.h-strench-btn::before {
content: '\f13a';
font-family: "Font Awesome 5 Free";
font-size: 27px;
font-weight: 600;
}
One more question. How can I change the height value(B) not element.style (A). Please compare the picture below. Please help thank you!

First step: make sure your querySelector calls are returning the correct elements. It will always return the first element that matches the selector.
As for the css: Height A is inline css, meaning it will always have priority over height B unless height B is marked !important. In order to revert to height B, height A must be removed entirely via remove property or simply set to null.
var obj = document.getElementById('name');
obj.style.removeProperty('height');
// if you want to return the old value...
// var oldValue = obj.style.removeProperty('height');
If you want to change contents in a stylesheet, see this example:
var declaration = document.styleSheets[0].rules[0].style;
var oldValue = declaration.removeProperty('height');
... However, be careful with the stylesheet example, as a change of indices can throw this off. It would be much safer to find an alternative that adds/removes classes with the values you desire instead.

Related

How to flip the height of the li elements to go up? [duplicate]

This question already has answers here:
Align inline-block DIVs to top of container element
(5 answers)
Closed 2 years ago.
Can anyone help me with how to set the height of those bars to start from bottom and go up. Currently its starting from top position and going downward?
I am using javascript to generate the li elements and setting its height to some values. Here is the code:
var rootContainer;
var barContainer;
var randomNumberButton;
var mergeSortButton;
const NUM_BARS = 70;
function initApp() {
rootContainer = document.querySelector(".algo-container");
barContainer = document.querySelector(".bar-container");
randomNumberButton = document.getElementById("random-number");
mergeSortButton = document.getElementById("merge-sort");
randomNumberButton.addEventListener("click", handleRandomNumberButton);
mergeSortButton.addEventListener("click", handleMergeSortButton);
}
function handleRandomNumberButton(e) {
barContainer.innerHTML = "";
let numbers = [];
for (let i = 0; i < NUM_BARS; i++) {
numbers.push(generateRandomNumber(340, 100));
}
let ul = document.createElement("ul");
ul.style.height = "100%";
ul.style.width = "100%";
for (let i = 0; i < NUM_BARS; i++) {
let li = document.createElement("li");
li.style.backgroundColor = "#576cff";
li.style.width = "8px";
li.style.marginLeft = "1px";
li.style.marginRight = "6px";
li.style.height = `${numbers[i]}px`;
ul.appendChild(li);
}
barContainer.appendChild(ul);
}
function handleMergeSortButton(e) {}
function generateRandomNumber(b, a) {
return Math.round(Math.random() * (b - a) + a);
}
window.addEventListener("DOMContentLoaded", initApp);
li {
display: inline-block
}
<div class="algo-container"></div>
<div class="bar-container"></div>
<input id="random-number" type="button" value="Random" />
<input id="merge-sort" type="button" value="Merge - sort" />
You can apply a CSS transform to the container of your stats:
.container {
transform: scaleY(-1);
}
This scales the height of the container with negative one, effectively reversing it.
Alternatively you can make the container a Flexbox and align it's items to the bottom:
.container {
display: flex;
align-items: flex-end;
}
Rotate your container by 180deg can do the trick. Use css transform like this on your container.
.your-container-class {
transform:rotate(180deg);
}

Problem with onclick firing to incrementally increase the size of each image in a for loop

I am trying to write a script in the head element of my web page where a loop of 10 images will successively increase by 5 pixels each when any one of the looped images is clicked. When the onclick fires, it does create a loop that correctly increases the size for each image, but unfortunately this output gets added to the end of the original loop instead of changing it.
In my head element script, I first tried to use getDocumentById(), then switched to passing the "this" reference to the function, but came up with the same result. I also tried to use addEventListener(), but this didn't work either.
In the head element:
<script>
function growingPumpkins(e) {
for (var i = 0; i < 10; i++) {
e.innerHTML += `<img src='bandit.png' style="width:${50 + i * 5}px; height:${50 + i * 5}px "/>`;
}
}
</script>
In the body element:
<section>
<h2>Growing Pumpkins</h2>
<p id="smashingPumpkins" onclick="growingPumpkins(this)" ></p>
<script>
for (var i = 0; i < 10; i++) {
document.getElementById("smashingPumpkins").innerHTML += "<img src='bandit.png' />";
}
</script>
</section>
I wanted to initially create an HTMLCollection in the head element to achieve the desired result, but wasn't able to get any output when trying that. Right now, I still get the onclick loop concatenated to the original loop.
In order to use Template Literals assertions ${foo} you need backticks, not quotes
`Text ${foo} text`
e.innerHTML += is used to append to the current HTML of an element, not to increase the size of an element.
Instead:No need for for loops. Use document.querySelectorAll() and NodeList.forEach() with Element.addEventListener() to attach a desired function to the Event handler
const grow = ev => {
const el = ev.currentTarget;
el._w = el._w ? el._w + 10 : 40;
el.style.width = `${el._w}px`;
};
document.querySelectorAll(".grow").forEach(el => el.addEventListener('click', grow));
.grow {
display: inline-block;
vertical-align: middle;
width: 30px;
transition: width 0.3s;
cursor: pointer;
}
<img class="grow" src="https://i.imgur.com/GDUJj6t.jpg">
<img class="grow" src="https://i.imgur.com/GDUJj6t.jpg">
Increment size of multiple images on parent click:
const el_backyard = document.querySelector("#backyard");
const tot = 5; // total images
const min_size = 30; // px
const step = 10; // increase by 10 px
// PLANT
for (let i=1; i<=tot; i++) {
const el = new Image();
el.src = "https://i.imgur.com/GDUJj6t.jpg";
el.style.width = `${step * i}px`;
el_backyard.append(el);
}
// GROW
const grow = ev => {
el_backyard.querySelectorAll('img').forEach(img => {
img.style.width = `${img.offsetWidth + step}px`;
});
};
el_backyard.addEventListener('click', grow);
#backyard > * {
display: inline-block;
vertical-align: middle;
transition: width 0.3s;
cursor: pointer;
}
<div id="backyard"></div>

recursively get CSS from an element and all the childs

So I would like to be able to do something like this:
getRecursiveCSS(document.getElementById('#menubar'))
And I would like it to return a string of CSS, for the main element and all the childs.
This is what I have tried: (does not work)
function fullPath(el){
var names = [];
while (el.parentNode){
if (el.id){
names.unshift('#'+el.id);
break;
}else{
if (el==el.ownerDocument.documentElement) names.unshift(el.tagName);
else{
for (var c=1,e=el;e.previousElementSibling;e=e.previousElementSibling,c++);
names.unshift(el.tagName+":nth-child("+c+")");
}
el=el.parentNode;
}
}
return names.join(" > ");
}
function styleRecursive(elements, css) {
elements = Object.prototype.toString.call(elements) === '[object Array]' ? elements: [elements];
if (elements.length == 0 || typeof elements[0] == 'undefined')
return css;
if (typeof elements[0].querySelector == 'undefined')
return css
if (typeof css == 'undefined')
css = fullPath(elements[0]) + '{' + getComputedStyle(elements[0]).cssText + '}';
else
css += fullPath(elements[0]) + '{' + getComputedStyle(elements[0]).cssText + '}';
_elements = [];
for (var i = 0; i < elements.length; i++) {
for (var ii = 0; ii < elements[i].childNodes.length; ii++)
_elements.push(elements[i].childNodes[ii]);
}
return styleRecursive(_elements, css);
};
i came up with a solution that maybe give you idea about how improve your code. In order to test driving this code I've made an element that have some children in different depths and this code traverse all children by their depth in recursive way to find/get their css. After that, all founded css plus the element name will storage in an object (JSON like) for later use.
Please Note:
1) This code is not bullet proof so you need to add a lot of conditions/checker to make it work for all kind of situations.
2) Tested in chrome.
3) Limited to classes for finding element and its children (easy to upgrade for ids and tags support)
Output:
one : {
display: "block",
position: "relative"
}
two : {
display: "inline-block",
font-family: "Montserrat"
}
three_1 : {
display: "table",
position: "absolute",
left: "0px"
}
four_1 : {
display: "table-cell",
position: "relative"
}
three_2 : {
display: "table",
position: "absolute",
right: "0px"
}
four_2 : {
display: "table-cell",
position: "relative"
}
HTML(Sample):
<div class="one">
<div class="two">
<div class="three_1">
<div class="four_1"></div>
</div>
<div class="three_2">
<div class="four_2"></div>
</div>
</div>
</div>
CSS(Sample):
.one {display:block;position:relative;}
.two {display:inline-block;font-family:'Montserrat';}
.three_1 {display:table;position:absolute;left:0;}
.three_2 {display:table;position:absolute;right:0;}
.four_1 {display:table-cell;position:relative;}
.four_2 {display:table-cell;position:relative;}
JS:
function convertObjlike(css) {
var s = {};
if (!css) return s;
css = css.split("; ");
for (var i in css) {
var l = css[i].split(": ");
s[l[0].toLowerCase()] = (l[1]);
}
return s;
}
function getCss(a) {
var sheets = document.styleSheets, o = {};
for (var i in sheets) {
var rules = sheets[i].rules || sheets[i].cssRules;
for (var r in rules) {
if (a === rules[r].selectorText) {
o = convertObjlike(rules[r].style.cssText);
}
}
}
return o;
}
var anObject = {};
function styleRecursive(element){
anObject[element.className] = (getCss('.'+element.className));
var children = element.children;
for (var i = 0; i < children.length; i++) {
styleRecursive(children[i])
}
}
styleRecursive( document.querySelector('.one') );
console.log(anObject);
Jsfiddle
EDIT: note that this solution returns the HTML copy, and not the CSS 'file'.
Here is my attempt. It gets every computed style of the element and stores it in the style attribute. It also removes the class attribute since in most cases it is used only for setting those styles (you can remove the removeAttribute call if you want). And it iterate over its children to compute recursively the resulting HTML.
The resulting HTML is huge, as a lot of styles are just the default value, and it isn't optimized for inherit styles, so every child gets all its styles again. Font faces must be imported/registred separately.
Hover effects and media queries does not get copied, since the getComputedStyle captures only the current state of the node. Relative units like vw, vh, %, etc, also gets fixed to the current absolute value. For the same reason, variables are lost and its values are used instead.
function getElemHtml(elem) {
let style = [], computed = window.getComputedStyle(elem)
for (const attr of computed) style.push(`${attr}:${computed[attr]}`)
let clone = elem.cloneNode()
clone.setAttribute('style', style.join(";"))
clone.removeAttribute('class')
let childrenHTML = ''
for (const child of elem.childNodes) childrenHTML += child.nodeType === Element.ELEMENT_NODE ? getElemHtml(child) : child.nodeType === Element.TEXT_NODE ? child.nodeValue : ''
clone.innerHTML = childrenHTML
return clone.outerHTML
}
const elem = document.querySelector("p")
const elemHtml = getElemHtml(elem)
document.querySelector("code").innerText = elemHtml
p {
width: 400px;
margin: 0 auto;
padding: 20px;
font: 2rem/2 sans-serif;
text-align: center;
background: purple;
color: white;
}
pre, code {
width: 90vw;
white-space: pre-wrap;
}
<p>Hello</p>
<pre><code></code></pre>

Can't access the style using DOM [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 5 years ago.
For some reason, the error keeps being thrown at me whenever I hover over the selected element. It seems like every time I use the .style property, it gives me this error. The problem is how I'm accessing the CSS element using .style. Is there another way of doing it?
Uncaught TypeError: Cannot read property 'style' of undefined
var image = document.getElementsByClassName("image");
var move = document.getElementsByClassName("link");
var description = document.getElementsByClassName("description");
for(var i = 0; i < move.length; i++){
move[i].addEventListener("click", function(){
if (move[i].style.marginLeft === "500px") {
move[i].style.marginLeft = "0";
} else {
move[i].style.marginLeft = "500px";
}
})
};
for(var i = 0; i<description.length;i++){
image[i].addEventListener("mouseover", function(){
description[i].style.visibility = visible;
description[i].style.opacity = 0;
var last = +new Date();
var tick = function(){
despcription[i].style.opacity = +despcription[i].style.opacity + (new Date() - last)/700;
last = +new Date();
if (+despcription[i].style.opacity < 1) {
(window.requestAnimationFrame && requestAnimationFrame(tick)) || setTimeout(tick, 16);
}
};
tick();
});
}
You have a typo: despcription
despcription[i].style.opacity = +despcription[i].style.opacity + (new Date() - last)/700;
last = +new Date();
if (+despcription[i].style.opacity < 1) {
And I don't know if you are trying to do more than this, but are you attempting to fade in an image on hover? If so, then setting this in CSS works well:
EDIT: I updated the sample to show a title under the image when you hover over the image. The key is the ~ (sibling) selector right here:
.image:hover~.image-title {
opacity: 1;
}
It says when the user hovers over the image class, then select the sibling element with class of .image-title and set its opacity to 1.
.image {
background-image: url('https://amazingslider.com/wp-content/uploads/2012/12/dandelion.jpg');
height: 300px;
width: 300px;
background-size: cover;
background-position: center;
}
.image:hover~.image-title {
opacity: 1;
}
.image-title {
opacity: 0;
transition: opacity 1s ease;
display: inline-block;
width: 300px;
text-align: center;
margin: 0;
}
<h1>Hover Over Image</h1>
<div class="image"></div>
<h3 class="image-title">Image Title</h3>

HTML text-overflow ellipsis detection

I have a collection of block elements on a page. They all have the CSS rules white-space, overflow, text-overflow set so that overflowing text is trimmed and an ellipsis is used.
However, not all the elements overflow.
Is there anyway I can use javascript to detect which elements are overflowing?
Thanks.
Added: example HTML structure I am working with.
<td><span>Normal text</span></td>
<td><span>Long text that will be trimmed text</span></td>
The SPAN elements always fit in the cells, they have the ellipsis rule applied. I want to detect when the ellipsis is applied to the text content of the SPAN.
Try this JS function, passing the span element as argument:
function isEllipsisActive(e) {
return (e.offsetWidth < e.scrollWidth);
}
Once upon a time I needed to do this, and the only cross-browser reliable solution I came across was hack job. I'm not the biggest fan of solutions like this, but it certainly produces the correct result time and time again.
The idea is that you clone the element, remove any bounding width, and test if the cloned element is wider than the original. If so, you know it's going to have been truncated.
For example, using jQuery:
var $element = $('#element-to-test');
var $c = $element
.clone()
.css({display: 'inline', width: 'auto', visibility: 'hidden'})
.appendTo('body');
if( $c.width() > $element.width() ) {
// text was truncated.
// do what you need to do
}
$c.remove();
I made a jsFiddle to demonstrate this, http://jsfiddle.net/cgzW8/2/
You could even create your own custom pseudo-selector for jQuery:
$.expr[':'].truncated = function(obj) {
var $this = $(obj);
var $c = $this
.clone()
.css({display: 'inline', width: 'auto', visibility: 'hidden'})
.appendTo('body');
var c_width = $c.width();
$c.remove();
if ( c_width > $this.width() )
return true;
else
return false;
};
Then use it to find elements
$truncated_elements = $('.my-selector:truncated');
Demo: http://jsfiddle.net/cgzW8/293/
Hopefully this helps, hacky as it is.
Adding to italo's answer, you can also do this using jQuery.
function isEllipsisActive($jQueryObject) {
return ($jQueryObject.width() < $jQueryObject[0].scrollWidth);
}
Also, as Smoky pointed out, you may want to use jQuery outerWidth() instead of width().
function isEllipsisActive($jQueryObject) {
return ($jQueryObject.outerWidth() < $jQueryObject[0].scrollWidth);
}
For those using (or planning to use) the accepted answer from Christian Varga, please be aware of the performance issues.
Cloning/manipulating the DOM in such a way causes DOM Reflow (see an explanation on DOM reflow here) which is extremely resource intensive.
Using Christian Varga's solution on 100+ elements on a page caused a 4 second reflow delay during which the JS thread is locked. Considering JS is single-threaded this means a significant UX delay to the end user.
Italo Borssatto's answer should be the accepted one, it was approximately 10 times quicker during my profiling.
Answer from italo is very good! However let me refine it a little:
function isEllipsisActive(e) {
var tolerance = 2; // In px. Depends on the font you are using
return e.offsetWidth + tolerance < e.scrollWidth;
}
Cross browser compatibility
If, in fact, you try the above code and use console.log to print out the values of e.offsetWidth and e.scrollWidth, you will notice, on IE, that, even when you have no text truncation, a value difference of 1px or 2px is experienced.
So, depending on the font size you use, allow a certain tolerance!
This sample show tooltip on cell table with text truncated. Is dynamic based on table width:
$.expr[':'].truncated = function (obj) {
var element = $(obj);
return (element[0].scrollHeight > (element.innerHeight() + 1)) || (element[0].scrollWidth > (element.innerWidth() + 1));
};
$(document).ready(function () {
$("td").mouseenter(function () {
var cella = $(this);
var isTruncated = cella.filter(":truncated").length > 0;
if (isTruncated)
cella.attr("title", cella.text());
else
cella.attr("title", null);
});
});
Demo: https://jsfiddle.net/t4qs3tqs/
It works on all version of jQuery
elem.offsetWdith VS ele.scrollWidth
This work for me!
https://jsfiddle.net/gustavojuan/210to9p1/
$(function() {
$('.endtext').each(function(index, elem) {
debugger;
if(elem.offsetWidth !== elem.scrollWidth){
$(this).css({color: '#FF0000'})
}
});
});
All the solutions did not really work for me, what did work was compare the elements scrollWidth to the scrollWidth of its parent (or child, depending on which element has the trigger).
When the child's scrollWidth is higher than its parents, it means .text-ellipsis is active.
When el is the parent element
function isEllipsisActive(el) {
let width = el.offsetWidth;
let widthChild = el.firstChild.offsetWidth;
return (widthChild >= width);
}
When el is the child element
function isEllipsisActive(event) {
let width = el.offsetWidth;
let widthParent = el.parentElement.scrollWidth;
return (width >= widthParent);
}
My implementation)
const items = Array.from(document.querySelectorAll('.item'));
items.forEach(item =>{
item.style.color = checkEllipsis(item) ? 'red': 'black'
})
function checkEllipsis(el){
const styles = getComputedStyle(el);
const widthEl = parseFloat(styles.width);
const ctx = document.createElement('canvas').getContext('2d');
ctx.font = `${styles.fontSize} ${styles.fontFamily}`;
const text = ctx.measureText(el.innerText);
return text.width > widthEl;
}
.item{
width: 60px;
overflow: hidden;
text-overflow: ellipsis;
}
<div class="item">Short</div>
<div class="item">Loooooooooooong</div>
If you're doing react, here's how I did it.
<div
ref={ref => {
if (!ref) return
const isOverflowing = ref.scrollWidth > ref.clientWidth
if (isOverflowing) {
// handle what to do next here
}
}}
/>
I think the better way to detect it is use getClientRects(), it seems each rect has the same height, so we can caculate lines number with the number of different top value.
getClientRects work like this
function getRowRects(element) {
var rects = [],
clientRects = element.getClientRects(),
len = clientRects.length,
clientRect, top, rectsLen, rect, i;
for(i=0; i<len; i++) {
has = false;
rectsLen = rects.length;
clientRect = clientRects[i];
top = clientRect.top;
while(rectsLen--) {
rect = rects[rectsLen];
if (rect.top == top) {
has = true;
break;
}
}
if(has) {
rect.right = rect.right > clientRect.right ? rect.right : clientRect.right;
rect.width = rect.right - rect.left;
}
else {
rects.push({
top: clientRect.top,
right: clientRect.right,
bottom: clientRect.bottom,
left: clientRect.left,
width: clientRect.width,
height: clientRect.height
});
}
}
return rects;
}
getRowRects work like this
you can detect like this
None of the solutions worked for me, so I chose a totally different approach. Instead of using the CSS solution with ellipsis, I just cut the text from a specific string length.
if (!this.isFullTextShown && this.text.length > 350) {
return this.text.substring(0, 350) + '...'
}
return this.text
and show "more/less" buttons if the length is exceeded.
<span
v-if="text.length > 350"
#click="isFullTextShown = !isFullTextShown"
>
{{ isFullTextShown ? 'show less' : 'show more' }}
</span>
Adding to #Дмытрык answer, missing deduction of borders and paddings to be fully functional!!
const items = Array.from(document.querySelectorAll('.item'));
items.forEach(item =>{
item.style.color = checkEllipsis(item) ? 'red': 'black'
})
function checkEllipsis(el){
const styles = getComputedStyle(el);
const widthEl = parseFloat(styles.width);
const ctx = document.createElement('canvas').getContext('2d');
ctx.font = `${styles.fontSize} ${styles.fontFamily}`;
const text = ctx.measureText(el.innerText);
let extra = 0;
extra += parseFloat(styles.getPropertyValue('border-left-width'));
extra += parseFloat(styles.getPropertyValue('border-right-width'));
extra += parseFloat(styles.getPropertyValue('padding-left'));
extra += parseFloat(styles.getPropertyValue('padding-right'));
return text.width > (widthEl - extra);
}
.item{
width: 60px;
overflow: hidden;
text-overflow: ellipsis;
}
<div class="item">Short</div>
<div class="item">Loooooooooooong</div>
The e.offsetWidth < e.scrollWidth solution is not always working.
And if you want to use pure JavaScript, I recommend to use this:
(typescript)
public isEllipsisActive(element: HTMLElement): boolean {
element.style.overflow = 'initial';
const noEllipsisWidth = element.offsetWidth;
element.style.overflow = 'hidden';
const ellipsisWidth = element.offsetWidth;
if (ellipsisWidth < noEllipsisWidth) {
return true;
} else {
return false;
}
}
For someone who uses e.offsetWidth < e.scrollWidth and got a bug that can show full text but still got ellipsis.
It because offsetWidth and scrollWidth always round the value. For example: offsetWidth return 161 but the actual width is 161.25.
The solution is use getBoundingClientRect
const clonedEl = e.cloneNode(true)
clonedElement.style.overflow = "visible"
clonedElement.style.visibility = "hidden"
clonedElement.style.width = "fit-content"
e.parentElement.appendChild(clonedEl)
const fullWidth = clonedElement.getBoundingClientRect().width
const currentWidth = e.getBoundingClientRect().width
return currentWidth < fullWidth
Case you are using line-clamp >= 2 line for adding ellipsis at more than one line you can use this conditioning:
if (
descriptionElement &&
descriptionElement.offsetHeight < descriptionElement.scrollHeight
) {
// has text-overflow
}
There's a small pixel problem with the answers above when comparing offsetWidth > scrollWidth.
W3C has a legacy API that returns element.scrollWidth value as rounded which is causing the comparison in some cases to to return false.
If the element width are 150px and the scrollWidth are 150.4px (rounded to 150), then this check will be returning false even if the browser are showing ellipsis in the text.
They have tried to update the APIs that return fractional pixels, but it was reverted due to webcompat.
There's a workaround using max-content and getClientRects().
Here's a sample code that I use onMouseEnter.
Note that this only works if the container has a boundary to 100% of the available width (so if you are using flexbox, your container has to be flex: 1 for example.
hasEllipsis(elementItem) {
let scrollWidth = elementItem.scrollWidth;
elementItem.style.width = 'max-content';
const itemRects = elementItem.getClientRects();
if (itemRects.length > 0 && itemRects[0].width > scrollWidth) {
scrollWidth = itemRects[0].width;
}
elementItem.style.width = 'auto';
return scrollWidth > elementItem.clientWidth;
}
Articles:
https://bugs.chromium.org/p/chromium/issues/detail?id=980476
https://github.com/w3c/csswg-drafts/issues/4123
The solution #ItaloBorssatto is perfect. But before looking at SO - I made my decision. Here it is :)
const elems = document.querySelectorAll('span');
elems.forEach(elem => {
checkEllipsis(elem);
});
function checkEllipsis(elem){
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const styles = getComputedStyle(elem);
ctx.font = `${styles.fontWeight} ${styles.fontSize} ${styles.fontFamily}`;
const widthTxt = ctx.measureText(elem.innerText).width;
if (widthTxt > parseFloat(styles.width)){
elem.style.color = 'red'
}
}
span.cat {
display: block;
border: 1px solid black;
white-space: nowrap;
width: 100px;
overflow: hidden;
text-overflow: ellipsis;
}
<span class="cat">Small Cat</span>
<span class="cat">Looooooooooooooong Cat</span>
there are some mistasks in demo http://jsfiddle.net/brandonzylstra/hjk9mvcy/ mentioned by https://stackoverflow.com/users/241142/iconoclast.
in his demo, add these code will works:
setTimeout(() => {
console.log(EntryElm[0].offsetWidth)
}, 0)

Categories