What I am trying to get is a text field of a fixed size (with a height of exactly one row) where you can type some text. If the text is short enough to fit in the text field it should just be displayed. But if the text is too long it should be displayed in a cycle, e.g. it constantly moves across the field and starts over. Like a continues sideways scroll of an infinite cyclic string, like you may have seen on news television. This should happen immediately after the user stops typing.
My question:
Is it possible to achieve this with just using html-textarea/css/js?
Maybe there is a way to smoothly scroll sideways through the typed text and seemlessly jump back to the beginning?
I can create a html textarea with all of properties that I need (like hidden scrollbar, fixed size, only horizontal scrolling) but I do not know how to make the text move in the above described way.
I am not limited to using the bulit-in textarea from html so if you have any other implementation ideas that can be done in html/css/js - they are welcomed.
Thank you!
No there isn't something native for that. This is a hacky solution. First detecting the overflow, but it's possible to hack it. For simplicity I'll assume length of string. Then when it "overflows" I shall rotate the string. It's important to reset when keydown again.
All in all, Maybe a more robust solution would be to rotate on blur and stop on focus.
var input = document.querySelector("input");
var int_id;
function debounce(func, timeout = 300) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, timeout);
};
}
function is_overflow(input) {
// for now:
return input.value.length >= 14
}
input.addEventListener('keydown', function() {
stop(input)
})
function stop(input) {
if (int_id) {
clearInterval(int_id)
input.value = input.getAttribute("data-value") || input.value
int_id = null;
}
}
input.addEventListener('input', debounce(function(ev) {
if (is_overflow(input)) {
input.setAttribute("data-value", input.value);
int_id = setInterval(function() {
rotate(input)
}, 100);
} else {
stop(input)
}
}))
function rotate(input) {
var str = input.value
var num = 1;
input.value = str.substr(num) + str.substr(0, num);
}
<input style="width:100px">
Related
So I have worked hard to get this code correct thus far. Basically my click event makes my shapes DECAY gradually start being affected. It works perfectly as I wanted. But my question is when I let go of holding down my mouse or finger it automatically jumps back to the original frame. Can I please get some help on how to make it gradually go back (or gradually end) like how it starts? that way its a fluid animation from start to finish.
Here's my Click event code
decaybackup = config.shader.decay;
world.resize()
});
let interval='';
let myshape = document.getElementById('shapeId');
myshape.addEventListener('pointerdown', function(event) {
interval = setInterval(()=>{
config.shader.decay += .001;
},1)
});
myshape.addEventListener('pointerup', function(event) {
config.shader.decay = decaybackup;
clearInterval(interval)
interval = '';
});
also here is a read only link to my site if you need a visual of what im talking about and can also see any code I have added...
enter link description here
THANK YOU!!!
Use setInterval() to decrement decay back to decaybackup similar to the way you increment it during pointerdown.
let undecayInterval;
myshape.addEventListener('pointerup', function(event) {
config.shader.decay = decaybackup;
clearInterval(interval);
clearInterval(undecayInterval);
undecayInterval = setInterval(() => {
config.shader.decay -= 0.001;
if (config.share.decay <= decaybackup) {
clearInterval(undecayInterval);
}, 1);
}
});
Lets say I add a text box of length of 50px, And I want to count the exact number of characters (including whitespace) that perfectly fits inside that text box, I mean no character should be allowed to be typed inside the textbox that require the sliding of whole line toward left; I mean, in another other-words, we need to disallow the typist to further insert any letter as the line reaches up to the length of the text box. Can we anyhow solve this by JavaScrip? Thanks for the help in advance, any help would be appreciated.
The whole logic is flawed as it would depend also on the size of the text inside the input. I'd put instead a limit of chars to be entered that don't go beyond. Using maxlength input attribute.
Anyways if you really wanna go this route, which I think is an overkill and not needed, then you can:
Make use of CanvasRenderingContext2D.measureText, docs here
In order to do that you'd have to create a hidden canvas element where to mimic your input text.
After that you will need to check on input if the text goes beyond the input width and avoid any further keystrokes but still allow for deletion.
Find attached an example snippet, not optimised, of what I am talking about.
const form = document.querySelector('#form'),
input = form.querySelector('input')
const createAppendCanvas = form => {
const canvas = document.createElement('Canvas')
form.appendChild(canvas)
}
createAppendCanvas(form)
const getTextMetrics = inputText => {
const canvas = document.querySelector('canvas'),
textWidth = Math.ceil(canvas.getContext('2d').measureText(inputText).width) + 10
return textWidth
}
const disableTyping = (event, input) => {
const inputText = event.target.value,
inputWidth = input.clientWidth
if (getTextMetrics(inputText) >= inputWidth) {
event.preventDefault()
return false
}
}
input.addEventListener('keypress', event => disableTyping(event, input))
input {
width: 50px;
}
canvas {
display: none;
}
<form id="form">
<input type="text" />
</form>
As #mel-macaluso rightly points out, this is a very big rabbit hole to go down, and the standard practice is to use the maxlength attribute to limit the number of characters.
*Edit: You can also set the width of the input using em, which is proportional to the font size. (The name em was originally a reference to the width of the capital M in the typeface and size being used, which was often the same as the point size ref) A combination of width in em and maxlength will give a very rough approximation of what you may be trying to achieve.
However if you really want to be able to limit input based text length, this would serve as a very simplistic example of how you might get started.
Edit: I recommend #mel-macaluso's answer: he added an example using CanvasRenderingContext2D.measureText(), which I suspect is much more efficient than getBoundingClientRect.
First some disclaimers:
This example doesn't take into account clipboard actions. That's a pretty big problem, and you'd be talking a lot more code to try to account for it (way beyond the scope of what can reasonably be done here).
It's also rather resource intensive. The process doing a getBoundingClientRect, forces the browser to reflow the document contents an extra time. Depending on the size of the page this can be a big deal, and it's not something to be done lightly.
var inp = document.getElementById('test');
// get font for input
var style = getComputedStyle(inp);
var maxWidth = inp.getBoundingClientRect().width;
var sizeTest = document.createElement('span');
// set font for span to match input
sizeTest.style.font = style.font;
inp.addEventListener('keydown', function(e){
if(e.ctrlKey || e.altKey) return;
if(e.key && e.key.length===1) {
sizeTest.textContent = inp.value;
document.body.append(sizeTest);
var w = sizeTest.getBoundingClientRect().width;
sizeTest.remove();
console.log(maxWidth, w, e.key, e.code);
if(w>maxWidth) e.preventDefault();
}
})
<input id='test'/>
So why is it so complex to do something like this? Fonts are tricky things. You have variable width (proportional) fonts, kerning, ligatures, etc. It's very complex, and browsers don't provide access to most of this information.
So if you want to know how long a segment of text is, you generally have to put it in a span with the same font settings and then request the bounding dimensions.
Here's neat solution using nested spans (with a contenteditable inner span) as a proxy input.
// Identifiers and dynamic styling
const innerSpan = document.querySelector("span.inner"),
outerSpan = document.querySelector("span.outer");
/* Threshold should be at least one character-width less than outerSpan.
(This formula was pretty close for my few tests;
for more precision and less flexibility, you can hard-code a value.) */
const estMaxCharWidth = innerSpan.offsetHeight / 1.7,
thresholdWidth = outerSpan.offsetWidth - estMaxCharWidth;
innerSpan.style.minWidth = `${Math.floor(thresholdWidth)-3}px`; // defaults to 0
innerSpan.style.minHeight = `${Math.floor(outerSpan.offsetHeight)-2}px`
// Listeners
innerSpan.addEventListener("focus", customOutline);
innerSpan.addEventListener("keydown", checkKeyAndWidth);
innerSpan.addEventListener("blur", removeOutlineAndHandleText);
// Functions
function checkKeyAndWidth(e){
// Runs when user presses a key, Conditionally prevents input
if(e.code == "Enter" || e.keyCode == 13){
e.preventDefault(); // Don't insert a new line
e.target.blur(); // (In production, set the focus to another element)
}
else{
// Some keys besides Enter are important, More could be added
const whitelistCodes = ["Backspace", "Tab", "Escape", "ArrowLeft", "ArrowRight", "Insert", "Delete"];
const whitelistKeyCodes = [8,9,27,37,39,45,46];
// If the inner span is wide enough, stop accepting characters
let acceptingCharacters = e.target.offsetWidth <= thresholdWidth;
if(!acceptingCharacters && !whitelistCodes.includes(e.code) && !whitelistKeyCodes.includes(e.keyCode) && !whitelistKeyCodes.includes(e.which)){
// Unauthorized incoming keystroke
e.preventDefault();
}
}
}
function customOutline(){
// Runs when span gets focus, Needed for accessibility due to CSS settings
outerSpan.style.borderColor = "DeepSkyBlue";
}
function removeOutlineAndHandleText(){
// Runs when focus is lost
outerSpan.style.borderColor = "Gray";
if(innerSpan.length < 1){ innerSpan.innerHTML = " "; } // force content
/* Since this is not a real input element, now might be the time to do something with the entered text */
}
.outer{
display: inline-block;
position: relative;
width: 100px; /* Defaults to 0 */
padding: 0;
border: 1px solid gray;
}
.inner{
display: inline-block;
position: relative;
top: 0;
height: 100%;
margin: 0;
outline: none; /* Don't do this without calling customOutline on focus */
}
<!-- requires that browser supports `contenteditable` -->
<span class="outer">
<!-- space character in innerSpan may improve cross-browser rendering -->
<span class="inner" contenteditable="true"> </span>
</span>
I'm creating dynamic scroll page, where I decided to use wheel event to detect user's scrolling. As you may know this event has a deltaY parameter that updates dynamically(several times for one scroll). I want the script to return true once deltaY is bigger than 100. However it updates over and over again causing my function shoot several times. Is there a way to 'bake'(be able to change once) this true value?
window.addEventListener('wheel', func)
function func(event){
if(event.deltaY>100){
var p = document.createElement('p');
var text = document.createTextNode("You scrolled over 100");
p.appendChild(text);
document.body.insertBefore(p, document.getElementsByTagName('p')[0]);
}
}
<p>Scroll the page down \/ and keep scrolling</p>
You could remove the listener once the element has been added. this way the function will not continue to fire.
window.addEventListener('wheel', func)
function func(event){
if(event.deltaY>100){
var p = document.createElement('p');
var text = document.createTextNode("You scrolled over 100");
p.appendChild(text);
document.body.insertBefore(p, document.getElementsByTagName('p')[0]);
window.removeEventListener('wheel',func);
}
}
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener
I'd like to detect in a web page when the user selects some text by dragging. However, there's one scenario in Windows which I'm calling a "double-click-drag" (sorry if there's already a better name I don't know) and I can't figure out how to detect it. It goes like this:
press mouse button
quickly release mouse button
quickly press mouse button again
drag with the button held down
This causes the dragging to select whole Words. It's quite a useful technique from the user perspective.
What I'm trying to do is tell the difference between a double-click-drag and a click followed by a separate drag. So when I get to step 2 I will get a click event but I don't want to treat it as a click yet; I want to see if they're about to immediately do step 3.
Presumably Windows detects this on the basis of the timing and how much the mouse has moved between step 2 and 3, but I don't know the parameters it uses so I can't replicate the windows logic. note that even if the mouse doesn't move at all between step 2 and 3, I still get a mousemove event.
I realise that I should be designing interfaces that are touch-friendly and device-neutral, and I have every intention of supporting other devices, but this is an enterprise application aimed at users on windows PCs so I want to optimize this case if I can.
We've done something similar. Our final solution was to create a click handler that suppressed the default response, and then set a global variable to the current date/time. We then set another function to fire in some 200ms or so that would handle the "click" event. That was our base function.
We then modified it to look at the global variable to determine when the last click occured. If it's been less than 200ms (modify based on your needs) we set a flag that would cause the click handler to fizzle and called a double click handler.
You could extend that approach by having your click and double click handlers manually fire the drag functionality.
I don't have access to the aforementioned code right now, but here is an example of that framework being used to track keyboard clicks to determine if a scanner or user has finished typing in a field:
var lastKeyPress = loadTime.getTime();
// This function fires on each keypress while the cursor is in the field. It checks the field value for preceding and trailing asterisks, which
// denote use of a scanner. If these are found it cleans the input and clicks the add button. This function also watches for rapid entry of keyup events, which
// also would denote a scanner, possibly one that does not use asterisks as control characters.
function checkForScanKeypress() {
var iVal = document.getElementById('field_id').value;
var currentTime = new Date()
var temp = currentTime.getTime();
if (temp - lastKeyPress < 80) {
scanCountCheck = scanCountCheck + 1;
} else {
scanCountCheck = 0;
}
lastKeyPress = currentTime.getTime();
}
// The script above tracks how many successive times two keyup events have occurred within 80 milliseconds of one another. The count is reset
// if any keypress occurs more than 80 milliseconds after the last (preventing false positives from manual entry). The script below runs
// every 200 milliseconds and looks to see if more than 3 keystrokes have occurred in such rapid succession. If so, it is assumed that a scanner
// was used for this entry. It then waits until at least 200 milliseconds after the last event and then triggers the next function.
// The 200ms buffer after the last keyup event insures the function is not called before the scanner completes part number entry.
function checkForScan() {
var currentTime = new Date();
var temp = currentTime.getTime();
if (temp - lastKeyPress > 200 && scanCountCheck > 3) {
FiredWhenUserStopsTyping();
scanCountCheck = 0;
}
setTimeout(checkForScan, 200);
}
Here is some code that I just wrote up based upon the above ideas. It's not tested and doesn't contain the actual drag events, but should give you a good starting point:
var lastClick = loadTime.getTime();
function fireOnClickEvent(event) {
event.preventDefault;
var currentTime = new Date()
var temp = currentTime.getTime();
if (temp - lastClick < 80) {
clearTimeout(tf);
doubleClickHandler();
} else {
tf = setTimeout(singleClickHandler, 100);
}
lastClick = currentTime.getTime();
}
function singleClickHandler() {
// Begin normal drag function
}
function doubleClickHandler() {
// Begin alternate drag function
}
A single double-click-drag action involves the following events in sequence:
mousedown -> mouseup -> click -> mousedown -> mousemove
With that in mind, I came up with this simple solution:
let maybeDoubleClickDragging = false;
let maybeDoubleClickDraggingTimeout;
const element = document.querySelector('#container');
element.addEventListener("click", function (e) {
maybeDoubleClickDragging = true;
element.removeEventListener("mousemove", handleMousemove);
});
element.addEventListener("mousedown", (e) => {
element.addEventListener("mousemove", handleMousemove);
if (maybeDoubleClickDragging) {
clearTimeout(maybeDoubleClickDraggingTimeout);
return;
}
});
element.addEventListener("mouseup", (event) => {
maybeDoubleClickDraggingTimeout = setTimeout(() => {
maybeDoubleClickDragging = false;
}, 200);
});
function handleMousemove(e) {
if(maybeDoubleClickDragging) {
element.textContent = 'you are double-click-dragging'
}
}
#container {
width: 300px;
height: 300px;
background: yellow;
}
<div id="container"></div>
I am working on homework that involves working with javascript. Part of my homework assignment is to use the event handlers onmouseout and onmouseouver. What is supposed to happen when the user hovers over a specific div element, the font size grows by 25%, and when the user mouses out of the div element, the font size goes back to normal. My question is, is it possible to incorporate both an onmouseover function and an onmouseout function into one function? Somehow that is what my teacher wants us to do. I have this started so far.
function FontSize(x)
{
x.style.fonstSize = large;
}
I'm also thinking this isnt the correct code to make the font 25% larger, but I'm not sure how to really incorporate an onmouseout in this function.
As a teacher myself, I am 99% sure that by "one function" the instructor means one general-purpose function to change the font size, not one function which uses conditional statements to work backwards and figure out whether it should be doing onmouseout or onmouseover.
Your script should contain:
function resize(elem, percent) { elem.style.fontSize = percent; }
Your HTML should contain:
<div onmouseover="resize(this, '125%')" onmouseout="resize(this, '100%')"
Text within div..
</div>
Note: Situations such as here, are exactly why JavaScript has the keyword "this"--to save us from needing to use complicated document.getElementById() statements.
You can use "%" property for controlling font-size as described here with the following code.
document.getElementById("div1").onmouseover = function() {
document.getElementById("div1").style.fontSize = "125%"
};
document.getElementById("div1").onmouseout = function() {
document.getElementById("div1").style.fontSize = "100%";
};
Here is the working jsfiddle : http://jsfiddle.net/LxhdU/
Yes you can. Call the same function on both events, and pass a parameter to indicate whether the fontsize should increase or decrease.
ChangeFontSize = function(element, shouldIncreaseFontsize)
{
var small=14;
var large = small * 1.25;
if(shouldIncreaseFontsize) {
element.style.fontSize = large + "px";
}
else {
element.style.fontSize = small + "px";
}
}
http://jsfiddle.net/TMHbW/1/
I'd do something simple like the following. The large and small values can be whatever you need them to be for the font size to work or they can be variables you've defined in prior code.
Demo: http://jsfiddle.net/lucuma/EAbYn/
function doHover(e) {
if (e.type=='mouseover') {
this.style.fontSize = "large";
} else {
this.style.fontSize = "small";
}
}
var el = document.getElementById('myelement')
el.onmouseout =doHover;
el.onmouseover=doHover;
It is possible you do not need to call both the events on the element explicitly instead extension you create will do that.Extend the Element's prototype. Jquery also does similar to this.
Ref Prototype
See Fiddle:- http://jsfiddle.net/4fs7V/
Element.prototype.hover= function( fnOver, fnOut ) {
this.onmouseover=fnOver;
this.onmouseout=fnOut || fnOver;
return this;
};
document.getElementById('test').hover(function(){
//do your mouseover stuff
},
function(){
//do your mouseout stuff
});
Update
Same can be achieved with just one function too:-
Hover me
.largeFont {
font-size:125%;
}
Element.prototype.hover = function (fnOver, fnOut) {
this.onmouseover = fnOver;
this.onmouseout = fnOut || fnOver;
return this;
};
document.getElementById('test').hover(changeMe);
function changeMe()
{
if(this.hasAttribute('class'))
{
this.removeAttribute('class');
}
else
{
this.setAttribute('class', 'largeFont');
}
}