Skip to content

Instantly share code, notes, and snippets.

@iiic
Last active November 28, 2019 16:13
Show Gist options
  • Save iiic/251c9fa16195cef1e0508bae018af6a6 to your computer and use it in GitHub Desktop.
Save iiic/251c9fa16195cef1e0508bae018af6a6 to your computer and use it in GitHub Desktop.
počítání znaků ve stylu Twitteru
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>počítačka znaků</title>
<style>
output.count-chars {
display: inline-block;
position: relative;
min-width: 30px;
min-height: 30px;
padding-top: 0;
border-radius: 50%;
margin: 0.5em 0 1em 0;
float: right;
}
output.count-chars::before {
content: '';
position: absolute;
width: calc(100% - (2 * 3px));
height: calc(100% - (2 * 3px));
margin: 3px;
background: #d6dbe0;
border-radius: 50%;
}
output.count-chars span {
position: relative;
display: inline-block;
z-index: 1;
text-align: center;
width: 100%;
margin-top: 5px;
}
</style>
</head>
<body>
<textarea class="max-length-counter-js 160">náhodný příklad</textarea>
<script>
( () =>
{
'use strict';
const OUTPUT_NODE_NAME = 'OUTPUT';
const OUTPUT_CLASS_NAME = 'count-chars';
const HIDE_RESULT_MORE_THAN = 99; // in chars
const GOOD_COLOR_MORE_THAN = 50; // in chars
const COLORS_MATRIX = {
'thermometerBackground': '#ccc',
'normal': 'orange',
'good': 'green',
'bad': 'red',
};
const createResultElementAfter = ( /** @type {HTMLInputElement | HTMLTextAreaElement} */ inputElement ) =>
{
const countElement = document.createElement( OUTPUT_NODE_NAME );
countElement.className = OUTPUT_CLASS_NAME;
const span = document.createElement( 'span' );
countElement.appendChild( span );
inputElement.parentNode.insertBefore( countElement, inputElement.nextSibling );
return countElement;
};
const drawVisualLengthInfoIn = ( /** @type {HTMLOutputElement} */ resultElement, /** @type {Number} */ textLength, /** @type {Number} */ maxChars ) =>
{
const PERCENT_100 = 100;
const remainingChars = maxChars - textLength;
if ( remainingChars <= HIDE_RESULT_MORE_THAN ) {
resultElement.lastElementChild.textContent = remainingChars;
} else {
resultElement.lastElementChild.textContent = '';
}
let percent = PERCENT_100 - ( ( PERCENT_100 / maxChars ) * remainingChars );
let color = COLORS_MATRIX.normal;
if ( percent > PERCENT_100 ) {
color = COLORS_MATRIX.bad;
percent = percent - PERCENT_100;
} else if ( textLength >= GOOD_COLOR_MORE_THAN ) {
color = COLORS_MATRIX.good;
}
resultElement.style.backgroundImage = 'linear-gradient(' + color + ' ' + percent + '%, ' + COLORS_MATRIX.thermometerBackground + ' 0)'; // polyfill for FF
resultElement.style.backgroundImage = 'conic-gradient(' + color + ' ' + percent + '%, ' + COLORS_MATRIX.thermometerBackground + ' 0)';
resultElement.setAttribute( 'aria-valuenow', String( textLength ) );
resultElement.setAttribute( 'aria-valuemax', String( maxChars ) );
resultElement.setAttribute( 'aria-valuemin', '0' );
resultElement.title = String( textLength ) + ' / ' + String( maxChars ) + ' (' + String( percent ) + '%)';
return true;
};
const recountChars = ( /** @type {HTMLInputElement | HTMLTextAreaElement} */ inputElement, /** @type {Number} */ maxChars ) =>
{
/** @type {HTMLOutputElement} */
const nes = inputElement.nextElementSibling;
/** @type {HTMLOutputElement} */
let resultElement;
if ( nes
&& nes.nodeType === Node.ELEMENT_NODE
&& nes.nodeName === OUTPUT_NODE_NAME
&& nes.classList.contains( OUTPUT_CLASS_NAME )
) {
resultElement = nes;
} else {
resultElement = createResultElementAfter( inputElement );
}
return drawVisualLengthInfoIn( resultElement, inputElement.value.length, maxChars );
};
HTMLTextAreaElement.prototype.recountChars = function ( /** @type {Number} */ maxChars )
{
return recountChars( this, maxChars );
};
//HTMLInputElement.prototype.recountChars = function ( … // possible but don't need now
window.addEventListener( 'load', () =>
{
Array.prototype.slice.call( document.getElementsByClassName( 'max-length-counter-js' ) ).forEach( ( /** @type {HTMLElement} */ element ) =>
{
element.classList.forEach( ( /** @type {String} */ singleClass ) =>
{
const maxChars = Number( singleClass );
if ( maxChars ) {
element.addEventListener( 'change', ( /** @type {Event} */ event ) =>
{
const eventTarget = event.target;
eventTarget.recountChars( maxChars );
}, false );
element.addEventListener( 'keydown', ( /** @type {Event} */ event ) =>
{
const eventTarget = event.target;
eventTarget.recountChars( maxChars );
}, false );
element.addEventListener( 'keyup', ( /** @type {Event} */ event ) =>
{
const eventTarget = event.target;
eventTarget.recountChars( maxChars );
}, false );
element.recountChars( maxChars );
return true;
}
return false;
} );
} );
}, false );
} )();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment