I have been unsuccessful in trying to add two seperate maximum character fields to a form. I have tried renaming the element ids and changing the var i and c but to no avail. Thanks
Fiddle http://jsfiddle.net/rLkcy0t4/
Here is the code.
var maxchar = 160;
var i = document.getElementById("textinput");
var c = document.getElementById("count");
c.innerHTML = maxchar;
i.addEventListener("keydown", count);
function count(e) {
var len = i.value.length;
if (len >= maxchar) {
e.preventDefault();
} else {
c.innerHTML = maxchar - len - 1;
}
}
textarea {
display: block;
width: 200 px;
height: 100 px;
}
Remaining characters: <span id="count"></span>
<textarea id="textinput">
</textarea>
Delegation and data attributes will help
window.addEventListener("load", function() {
const container = document.getElementById("container")
container.querySelectorAll("textarea").forEach(ta => ta.dataset.max = ta.dataset.cnt); // save
container.addEventListener("input", function(e) {
const tgt = e.target;
if (tgt.matches("textarea")) {
const maxchar = +tgt.dataset.max;
let cnt = +tgt.dataset.cnt;
const c = tgt.previousElementSibling;
cnt = maxchar - tgt.value.length;
if (cnt < 0) {
tgt.value = tgt.value.slice(0, maxchar);
cnt = 0;
}
tgt.dataset.cnt = cnt;
c.textContent = cnt;
}
})
})
textarea {
display: block;
width: 200 px;
height: 100 px;
}
<div id="container">
Remaining characters: <span class="count"></span><textarea id="textinput1" data-cnt="160"></textarea>
<hr/> Remaining characters: <span class="count"></span><textarea id="textinput2" data-cnt="100"></textarea>
</div>
The problem in your fiddle is you are declaring multiple variable with the same name. You declare i and c twice. You also declare the function count twice. Rename half of those variables to something unique and it should work how you expect it to.
You can see a working example in this fiddle.
Related
I am trying to put three elements that contain text on one line using only HTML tags and the style property. One of the elements is a counter that is counting up. Unfortunately the elements are too far from each other in my solution as I am trying to get them to stick together seamlessly. Cold you please help me out?
FYI: I have read several posts here on SO before posting and tried my best to make a solution below.
<span style="display: flex; justify-content: space-between;">
<span> There is </span>
<strong><span class="counter" style="font-family:Courier New; style:bold" data-target="100">0</span></strong>
<span>kg spam.</span>
</span>
<script>
const counters = document.querySelectorAll('.counter');
for(let n of counters) {
const updateCount = () => {
const target = + n.getAttribute('data-target');
const count = + n.innerText;
const speed = 5000; // change animation speed here
const inc = target / speed;
if(count < target) {
n.innerText = Math.ceil(count + inc);
setTimeout(updateCount, 1);
} else {
n.innerText = target;
}
}
updateCount();
}
</script>
Removing justify-content: space-between; and adding padding to the left and right of the counter should work:
<span style="display: flex;">
<span>There is </span>
<strong><span class="counter" style="font-family: Courier New; style: bold; padding: 0 0.2em" data-target="100">0</span></strong>
<span> kg spam.</span>
</span>
<script>
const counters = document.querySelectorAll('.counter');
for(let n of counters) {
const updateCount = () => {
const target = + n.getAttribute('data-target');
const count = + n.innerText;
const speed = 5000; // change animation speed here
const inc = target / speed;
if(count < target) {
n.innerText = Math.ceil(count + inc);
setTimeout(updateCount, 1);
} else {
n.innerText = target;
}
}
updateCount();
}
</script>
You might need to tweak the margin size, but this is the best idea I came up with.
If your goal is to form a sentence then you don't need these many spans they are all inline elements. I think one span is enough:
<style>
.counter {
style="font-family:Courier New;"
}
</style>
<span>
There is <em class="counter" data-target="100">0</em>kg spam.
And <em class="counter" data-target="200">0</em> buns.
</span>
<script>
const counters = document.querySelectorAll('.counter');
for (let n of counters) {
const updateCount = () => {
const target = +n.getAttribute('data-target');
const count = +n.innerText;
const speed = 5000; // change animation speed here
const inc = target / speed;
if (count < target) {
n.innerText = Math.ceil(count + inc);
setTimeout(updateCount, 15);
} else {
n.innerText = target;
}
}
updateCount();
}
</script>
If screen width reduces the line will wrap automatically. Same text with 100px wide box wraps perfectly. You don't have to do anything:
<style>
.counter {
style="font-family:Courier New;"
}
div{
width: 100px;
border: 1px solid gray;
}
</style>
<div><span>
There is <em class="counter" data-target="100">0</em>kg spam.
And <em class="counter" data-target="200">0</em> buns.
</span></div>
<script>
const counters = document.querySelectorAll('.counter');
for (let n of counters) {
const updateCount = () => {
const target = +n.getAttribute('data-target');
const count = +n.innerText;
const speed = 5000; // change animation speed here
const inc = target / speed;
if (count < target) {
n.innerText = Math.ceil(count + inc);
setTimeout(updateCount, 15);
} else {
n.innerText = target;
}
}
updateCount();
}
</script>
in chrome, space-around doesn't center items if its single column.
but in Firefox, it works.
how to make it behave like firefox?
also, keep in mind that text is aligned to the right
.flex-cont {
display: flex;
justify-content: flex-start;
flex-flow: column wrap;
align-content: space-around;
align-content: space-evenly;
align-items: flex-end;
}
.flex-item {
/* display: inline-block; */
flex: 0 1 auto;
width: fit-content;
}
http://jsfiddle.net/f6k7xoe0/1/
edit: also I can do this but this messes up text aligning to right :
.flex-cont{
align-items: center;
}
edit: honestly I wouldn't care so much if it was as a hobby, but I added cefsharp(chrome) in my application. will be in production. there is no other way. i have to get that render in the cefsharp.
edit:
this is not a duplicate.
I dont ask WHY it doesn't work. I want a solution
my output is different. output in the other questions is not even multi-column.
edit2: I solved it via js getboundrect compare get max-width of each item them apply margin if wrap happens. but its messy don't wanna use it. but I have to.
I cleaned up the code to make it apply the all flex-container, flex item if you give appropriate CssSelector in the doit() function. it will work. but this is for columns.
http://jsfiddle.net/yeaqrh48/1203/
var debug = true;
class ertTimer {
constructor(funcName ,intervalms=3500, maxRunDuration=20000 , StopIfReturnsTrue=true ) {
this.intervalObj = setInterval(function(){
console.log("interval - funcName:" + funcName.name);
try{
var res = funcName();
if(StopIfReturnsTrue)
if(res == true)
clearInterval(intervalObj);
} catch(exx){console.warn(exx.message, exx.stack);}
}, intervalms);
// after 15 sec delete interval
setTimeout( function(){ clearInterval( intervalObj ); },maxRunDuration);
this.intervalms = intervalms;
this.maxRunDuration = maxRunDuration;
}
get getter_intervalms() { return this.intervalms; }
calcRepeatTimes() {
return this.maxRunDuration / this.intervalms;
}
}
var center_ONsingleCol_nonFF = function(contNode, itemSelector) {
var items = contNode.querySelectorAll(itemSelector);
//arr.count shoud be 1 element // items[0].style.alignItems = "center";
var parItem = items[0].parentNode;
var parItemR = parItem.getBoundingClientRect();
var parWidth = parItemR.width;
var maxItemWidth = 0;
for (var k = 0; k < items.length; k++) {
var currItem = items[k].getBoundingClientRect();
if (currItem.width > maxItemWidth)
maxItemWidth = currItem.width;
//console.log(parWidth, itemWidth);
}
var alignItemsVal = getComputedStyle_propValue(parItem , "align-items");
var flexDirVal = getComputedStyle_propValue(parItem , "flex-direction");
var iswrapped = isWrapped(contNode ,itemSelector );
for (var k = 0; k < items.length; k++) {
if(iswrapped && flexDirVal == "column" ){
if(alignItemsVal == "flex-end"){
items[k].style.marginRight = "" + ((parWidth - maxItemWidth) * 0.5) + "px";
items[k].style.marginLeft = "";
}
else if(alignItemsVal == "flex-start"){
items[k].style.marginRight = "";
items[k].style.marginLeft = "" + ((parWidth - maxItemWidth) * 0.5) + "px";
}else
{
items[k].style.marginRight = "";
items[k].style.marginLeft = "";
}
}
else{
items[k].style.marginRight = "";
items[k].style.marginLeft = "";
}
}
};
var getComputedStyle_propValue = function(element , CssPropName){
//var element = document.querySelector( selector );
var compStyles = window.getComputedStyle(element);
var comStyle_xxx = compStyles.getPropertyValue(CssPropName);
return comStyle_xxx;
};
var colorizeItem = function(items) {
for (var k = 0; k < items.length; k++) {
items[k].style += ";background:Red;";
}
};
var detectWrap = function(contNode, item_selector) {
var wrappedItems = [];
var prevItem = {};
var currItem = {};
var items = contNode.querySelectorAll(item_selector);
//console.log("wrapped item arrrat::",items);
for (var i = 0; i < items.length; i++) {
currItem = items[i].getBoundingClientRect();
if (prevItem && prevItem.top > currItem.top) {
wrappedItems.push(items[i]);
}
prevItem = currItem;
}
return wrappedItems;
};
var isFirefox = function() {
var _isFF = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
return _isFF;
};
var isWrapped = function(contNode, item_selector){
var wrappedItems = detectWrap(contNode, item_selector);
//colorizeItem(wrappedItems);
if (wrappedItems == null || wrappedItems.length == 0)
return true;
else
return false;
};
var isWired_listenContResize = false;
var doit = function() {
if (isFirefox()) {
console.log("ff already works Right. ");
return;
} else {
console.log("not ff. script will run. ");
}
/* flexItem_selector must be relative to flexCont*/
var flexContainer_selector = ".flex-cont.cont-resize"; /*specific flex-cont */
var flexItem_selector = ".flex-item";
var contList = document.querySelectorAll(flexContainer_selector);
for (var i = 0; i < contList.length; i++) {
//no such event //there is external lib..
// call doit after you change size in the code;
if (!isWired_listenContResize) {
contList[i].onresize = function() { doit(); };
}
center_ONsingleCol_nonFF(contList[i], flexItem_selector);
}
isWired_listenContResize = true;
};
window.onresize = function(event) { doit(); };
window.onload = function(event) {
doit();
const et1_ = new ertTimer(doit , 500, 320000,true );
};
From the https://gist.github.com/oshliaer/d468759b3587cfb424348fa722765187 , It is possible to select a particular word from the findText, I want to implement the same for bold words only
I have a function to find bold. How do I modify the above gist?
var startFlag = x;
var flag = false;
for (var i = x; i < y; i++) {
if (text.isBold(i) && !flag) {
startFlag = i;
flag = true;
} else if (!text.isBold(i) && flag) {
flag = false;
rangeBuilder.addElement(text, startFlag, i - 1);
doc.setSelection(rangeBuilder.build());
return;
}
}
if (flag) {
rangeBuilder.addElement(text, startFlag, i - 1);
doc.setSelection(rangeBuilder.build());
return;
}
Let's assume another algorithm
/*
* #param {(DocumentApp.ElementType.LIST_ITEM | DocumentApp.ElementType.PARAGRAPH)} element
*/
function hasBold(element, start) {
var text = element.editAsText();
var length = element.asText().getText().length;
var first = -1;
var end = -1;
while (start < length) {
if (first < 0 && text.isBold(start)) {
first = start;
}
if (first > -1 && !text.isBold(start)) {
end = start - 1;
return {
s: first,
e: end
}
}
start++;
}
if (first > -1) {
return {
s: first,
e: length - 1
}
}
return false;
}
It's not clean but I've tested it and it works fine.
hasBold lets us finding bolds in the current element.
Finally, we have to loop this feature within document.getBody().
You could to get the full code here find next bold text in google document.
Also you could try it on a copy
A new idea
The Direct searcing
The best way is to use a callback while it is checked
var assay = function (re) {
var text = re.getElement()
.asText();
for (var offset = re.getStartOffset(); offset <= re.getEndOffsetInclusive(); offset++) {
if (!text.isBold(offset)) return false;
}
return true;
}
function findNextBold() {
var sp = 'E.';
Docer.setDocument(DocumentApp.getActiveDocument());
var rangeElement = Docer.findText(sp, Docer.getActiveRangeElement(), assay);
rangeElement ? Docer.selectRangeElement(rangeElement) : Docer.setCursorBegin();
}
The Approx searching
var assay = function(re) {
var text = re.getElement().asText();
var startOffset = re.getStartOffset();
var endOffset = re.getEndOffsetInclusive() + 1;
for (var offset = startOffset; offset < endOffset; offset++) {
if (!text.isBold(offset)) return false;
}
return this.test(text.getText().slice(startOffset, endOffset));
}
function findNextBold() {
var searchPattern = '[^ ]+#[^ ]+';
var testPattern = new RegExp('^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$');
Docer.setDocument(DocumentApp.getActiveDocument());
var rangeElement = Docer.findText(searchPattern, Docer.getActiveRangeElement(), assay.bind(testPattern));
rangeElement ? Docer.selectRangeElement(rangeElement) : Docer.setCursorBegin();
}
Docer
Yes. it is possible to find bold text. You need to use findText(searchPattern) to search the contents of the element for the specific text pattern using regular expressions. The provided regular expression pattern is independently matched against each text block contained in the current element. Then, use isBold() to retrieve the bold setting. It is a Boolean which returns whether the text is bold or null.
I'm trying to dynamically populate a select and call a truncate function in the loop... like below. I want to send the option text down to the function, truncate it if it's longer than 20 chars and send it back before it gets added to the option and appended to the select.
$(function() {
for (var i = 0; i < response.option.length; i++) {
var truncatedText = truncate();
var text = response.option[i].name;
truncate(text);
$("select").append("<option>" + truncatedText.text + "</option>");
}
});
function truncate(text) {
var textLength = text.length;
if (textLength > 20) {
text = text.substr(0, 20) + '...';
}
return text;
}
After jsfiddling for a while I landed on a working solution. Is there a more elegant way to do this?
https://jsfiddle.net/kirkbross/pcb0a3Lg/9/
var namesList = ['johnathan', 'tim', 'greggory', 'ashton', 'elizabeth'];
$(function() {
for (var i = 0; i < 5; i++) {
var name = namesList[i];
$('#names').append('<option>' + name + '</option>');
}
var selected_option = $('#names').find('option:selected').val();
var truncated = truncate(selected_option);
$('option:selected').text(truncated.new);
$('#names').change(function(){
var selected_option = $(this).find('option:selected').val();
var truncated = truncate(selected_option);
$('option:selected').text(truncated.new);
});
});
function truncate(selected_option) {
var nameLength = selected_option.length
if (nameLength > 4) {
selected_option = selected_option.substr(0, 4) + '...';
}
return {new: selected_option}
}
I am currently expanding and collapsing the height of a text area on the keyupevent. However I want the text area also to initialise it's height once the value is binded to the text area via a knockout custom binding. Any solutions?
(With the use of only javascript) (Without adding any jquery libraries)
Current key up handelling code
var textElement = $textBox.get(0);
var textElementOriginalHeight = $textBox.height();
while ($textBox.height() > textElementOriginalHeight && textElement.scrollHeight < textElement.offsetHeight) {
$textBox.height($textBox.height() - 1);
}
var h = 0;
while (textElement.scrollHeight > textElement.offsetHeight && h !== textElement.offsetHeight) {
h = textElement.offsetHeight;
$textBox.height($textBox.height() + 1);
}
You're gonna need to register a custom binding-handler to do that. Something like:
(function(ko)
{
function handleAutoFit(textElement, val)
{
if (!textElement.value)
textElement.value = val;
var $textBox = $(textElement);
var textElementOriginalHeight = $textBox.height();
while ($textBox.height() > textElementOriginalHeight && textElement.scrollHeight < textElement.offsetHeight) {
$textBox.height($textBox.height() - 1);
}
var h = 0;
while (textElement.scrollHeight > textElement.offsetHeight && h !== textElement.offsetHeight) {
h = textElement.offsetHeight;
$textBox.height($textBox.height() + 1);
}
}
ko.bindingHandlers.autoFit = {
update: function (element, valueAccessor) {
var val = ko.unwrap(valueAccessor());
handleAutoFit(element, val);
}
};
})(ko);
HTML:
<textarea data-bind="autoFit: someObservable, value: someObservable, valueUpdate: 'afterkeydown'"></textarea>
Or if you're using the Knockout 3.1 and above:
<textarea data-bind="autoFit: someObservable, textInput: someObservable"></textarea>
See Fiddle