Skip to content

Instantly share code, notes, and snippets.

@Sjeiti
Forked from anonymous/fiddle.html
Last active March 18, 2018 12:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Sjeiti/ea70db7ade56efd0b76fac176067145b to your computer and use it in GitHub Desktop.
Save Sjeiti/ea70db7ade56efd0b76fac176067145b to your computer and use it in GitHub Desktop.
Javascript solution for middle ellipsis (see: https://jsfiddle.net/Sjeiti/cxsqv50n/)
<main>
<h1 data-middle-ellipsis>A Javascript solution to middle ellipsis</h1>
<div data-middle-ellipsis>The quick fox</div>
<div data-middle-ellipsis class="inline-block">The lazy dog</div>
<div data-middle-ellipsis>theQuickBrownFoxJumpsOverTheLazyDog</div>
<div data-middle-ellipsis class="bold">The quick brown fox jumps over the lazy dog</div>
<div data-middle-ellipsis class="small">The quick brown fox jumps over the lazy dog</div>
<div data-middle-ellipsis class="small bold">The quick brown fox jumps over the lazy dog</div>
<div data-middle-ellipsis class="large">The quick brown fox jumps over the lazy dog</div>
<div data-middle-ellipsis>The quick brown fox jumps over the lazy dog</div>
<div data-middle-ellipsis="70">The quick brown fox jumps over the lazy dog</div>
<div data-middle-ellipsis="30">The quick brown fox jumps over the lazy dog</div>
<div data-middle-ellipsis="1">The quick brown fox jumps over the lazy dog</div>
<div data-middle-ellipsis="99">The quick brown fox jumps over the lazy dog</div>
<div data-middle-ellipsis="-3">The quick brown fox jumps over the lazy dog</div>
<div data-middle-ellipsis="-3">The quick brown dog</div>
<div data-middle-ellipsis="0">The quick brown dog</div>
<div data-middle-ellipsis="-1">The quick brown dog</div>
<div data-middle-ellipsis="-99">The quick brown dog</div>
</main>
/**
* Attibute modifier to create middle ellipsis
* When the attribute value is left blank the ellipsis will be in the middle
* When positive the attribute value will be used as a percentage
* When negative the attribute value will be used as character index counted from the end
* @example
* <div data-middle-ellipsis>A Javascript solution to middle ellipsis</div>
* <div data-middle-ellipsis="20">A Javascript solution to middle ellipsis</div>
* <div data-middle-ellipsis="-3">A Javascript solution to middle ellipsis</div>
*/
const attributeName = 'data-middle-ellipsis'
const attributeTextName = attributeName+'-text'
const attributeFontName = attributeName+'-font'
const clamp = (val,min=Number.NEGATIVE_INFINITY,max=Number.POSITIVE_INFINITY)=>Math.max(min,Math.min(max,val))
const ellipsis = '…'
const map = new Map()
let timeoutId
testMiddleEllipsis()
window.addEventListener('resize', onResize, { capture: true, passive: true })
function onResize(e){
clearTimeout(timeoutId)
timeoutId = setTimeout(testMiddleEllipsis, 200)
}
function testMiddleEllipsis() {
Array.from(document.querySelectorAll(`[${attributeName}]`)).forEach(elm=>{
// do not recalculate variables a second time
const mapped = map.get(elm)
let {text, textLength, from, multiplier, font, textWidth, elementWidth} = mapped||{}
// first time
if (!mapped) {
text = elm.textContent
textLength = text.length
from = parseFloat(elm.getAttribute(attributeName))||50
multiplier = from>0&&from/100
const computedStyle = window.getComputedStyle(elm, null)
font = `${computedStyle.getPropertyValue('font-weight')} ${computedStyle.getPropertyValue('font-size')} ${computedStyle.getPropertyValue('font-family')}`
textWidth = getTextWidth(text, font)
elementWidth = elm.offsetWidth
map.set(elm, {text, textLength, from, multiplier, font, textWidth, elementWidth})
}
//
const {offsetWidth} = elm
const widthChanged = !mapped||elementWidth!==offsetWidth
mapped&&widthChanged&&(mapped.elementWidth=elementWidth=offsetWidth)
//
if (widthChanged&&textWidth>elementWidth) {
elm.setAttribute('title', text)
let smallerText = text
let smallerWidth = elementWidth
while(smallerText.length>3){
let smallerTextLength = smallerText.length
const half = multiplier&&
clamp(multiplier*smallerTextLength<<0,1,smallerTextLength-2)||
Math.max(smallerTextLength+from-1,1)
const half1 = smallerText.substr(0,half).replace(/\s*$/,'')
const half2 = smallerText.substr(half+1).replace(/^\s*/,'')
smallerText = half1+half2
smallerWidth = getTextWidth(smallerText+ellipsis, font)
if (smallerWidth<elementWidth) {
elm.textContent = half1+ellipsis+half2
break;
}
}
}
})
}
/**
* Get the text width
* @param {string} text
* @param {string} font
*/
function getTextWidth(text, font) {
let context = getTextWidth.context
if (!context) {
const canvas = document.createElement('canvas')
context = getTextWidth.context = canvas.getContext('2d')
}
context.font = font
const metrics = context.measureText(text)
return metrics.width
}
$width: 130px;
html,body{
width: 100%;
height: 100%;
line-height: 170%;
}
main {
width: 100%;
height: 100%;
font-family: Arial;
background: #fff linear-gradient(90deg
,transparent $width
,#f0f0f0 $width
,#f0f0f0 2*$width
,transparent 2*$width
);
}
[data-middle-ellipsis]{
max-width: $width;
white-space: nowrap;
box-shadow: -1px 0 0 red inset;
}
.inline-block { display: inline-block; }
.bold { font-weight: bold; }
.small { font-size: 10px; }
.large { font-size: 32px; }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment