I use the code below with HTML5 pattern matching on the input boxes and the CSS3 :invalid and :valid pseudo-selectors to display the output of the validation (valid value or not) in the div.input-validation available next to the input box. This works as expected but it displays the validation mark (✘ - invalid input) during page load itself and on re-loading the page. How should I avoid this?
Code:
<style type="text/css">
input {
font-size: 1em;
padding: .3em;
border-radius: 3px;
margin: .2em;
}
input[type="text"]:valid {
color: green;
}
input[type="text"]:valid ~ .input-validation::before {
content: "\2714";
color: green;
}
input[type="text"]:invalid ~ .input-validation::before {
content: "\2718";
color: red;
}
.input-validation {
display: inline-block;
}
</style>
<?echo $passwordregister;?>
<input name="pw" autocomplete="off" type="text" id="pw" pattern="[a-zA-Z0-9]{6,22}" autofocus required >
<div class="input-validation"></div>
You can hide the invalid value (✘) symbol on page load using either one of the following options.
Option 1: Hide the span which contains the symbol on page load and display it only when some keypress event has happened on the input text box.
window.onload = function() {
var el = document.querySelectorAll("input[type='text']");
for (var i = 0; i < el.length; i++) {
el[i].onkeypress = showSymbol;
}
function showSymbol() {
this.nextElementSibling.style.display = "inline-block"; // display the span next to the input in which key was pressed.
}
}
input {
font-size: 1em;
padding: .3em;
border-radius: 3px;
margin: .2em;
}
input[type="text"]:valid {
color: green;
}
input[type="text"]:valid + .input-validation::before {
content: "\2714";
color: green;
}
input[type="text"]:invalid + .input-validation::before {
content: "\2718";
color: red;
}
.input-validation {
display: none;
}
<input name="pw" autocomplete="off" type="text" id="pw" class="new" pattern="[a-zA-Z0-9]{6,22}" autofocus required/> <span class="input-validation"></span>
Option 2: Define the CSS rules based on the presence of certain class (say visited) and assign this class only when some key is pressed in the input box.
window.onload = function() {
var el = document.querySelectorAll("input[type='text']");
for (var i = 0; i < el.length; i++) {
el[i].onkeypress = showSymbol;
}
function showSymbol() {
this.classList.add("visited"); // add the visited class
}
}
input {
font-size: 1em;
padding: .3em;
border-radius: 3px;
margin: .2em;
}
input[type="text"].visited:valid {
color: green;
}
input[type="text"].visited:valid + .input-validation::before {
content: "\2714";
color: green;
}
input[type="text"].visited:invalid + .input-validation::before {
content: "\2718";
color: red;
}
.input-validation {
display: inline-block;
}
<input name="pw" autocomplete="off" type="text" id="pw" class="new" pattern="[a-zA-Z0-9]{6,22}" autofocus required/> <span class="input-validation"></span>
Note:
I have replaced the ~ in your CSS selectors with + because ~ selects all siblings which match the selector whereas the + selects only the adjacent sibling. Using ~ would make the span next to all input boxes get displayed (when you have multiple input boxes in the form) as soon as you type a value in the first.
I have also modified the .input-validation from div to span but that is more of a personal preference and you can just retain the original div itself without any difference in functionality.
Related
I am creating an interactive programming website where users can type 3 commands: turn right, turn left and forward n. <textarea> to type commands:
<textarea name="codeEditor" id="codeEditor" ></textarea>
Text is white. If user types any of the commands then text should turn orange. How to accomplish this with <textarea>?
This approach uses an <input> element for entry and a list to display the command history. There is a transition to highlight the previous command and an error message to help the user.
const entry = document.getElementById("entry");
const error = document.getElementById("error");
const history = document.getElementById("history");
let last = document.getElementById("last");
let prev;
entry.addEventListener("change", evt => {
const valid = evt.target.checkValidity()
error.classList.toggle("hidden", valid);
if (valid) {
prev = document.createElement("li");
prev.textContent = entry.value;
history.insertBefore(prev, last);
last = prev;
entry.value = '';
}
});
* {
margin: 0;
padding: 0;
font-family: verdana, sans-serif;
font-size: 28px;
background-color: black;
}
#history {
list-style: none;
height: 9rem;
color: white;
}
#history li {
color: white;
transition: color 2s;
}
#history li:first-child {
color: #0ff;
}
#entry {
background-color: black;
color: #2f2;
border: 1px solid white;
outline: none;
}
.hidden {
visibility: hidden;
}
.error {
font-size: .5rem;
color: red;
}
<input type="text" id="entry" placeholder="command" pattern="(turn (left|right)|forward \d+)" required>
<span id="error" class="error hidden">'turn left', 'turn right' or 'forward #'</span>
<ul id="history">
<li id="last"></li>
</ul>
The problem is that when I cut the text and the program reaches the "cut" event listener it never passes the if statement because inputField.innerHTML still is reading the previous value.
How could I overcome this behavior?
I have not idea how to google this, so I thought having it analyzed and explained by a human being would be a viable solution.
Thanks!
const inputField = document.querySelector(".input-field");
inputField.addEventListener("cut", () => {
console.log("getSelection: " + document.getSelection());
console.log("innerHTML: " + inputField.innerHTML);
if (inputField.innerHTML === "") {
console.log("I am here");
}
});
.input-field {
min-height:35px;
width: 80%;
font-size: 16px;
padding: 5px;
margin:5px;
border: 2px solid #e6e6e6;
}
[contenteditable="true"]:empty:before {
content: attr(placeholder);
pointer-events: none;
display: block; /* For Firefox */
}
<div class="input-field" placeholder="Tell me something..." contentEditable="true"></div>
A more reliable solution is to compare the empty string with the textContent not innerHTML as the last contains an <br> element anyway, that's why the condition is not matching. Also, the event and DOM update does not run in the same microtask, so the updated content should be retrieved asynchronously, with queueMicrotask, or requestAnimationFrame for example:
const inputField = document.querySelector(".input-field");
inputField.addEventListener("cut", () => {
console.log(inputField.innerHTML);
requestAnimationFrame(() => {
if (/^\n?$/.test(inputField.textContent)) {
console.log("I am here");
}
});
});
.input-field {
min-height:35px;
width: 40%;
font-size: 16px;
padding: 5px;
margin:5px;
border: 2px solid #e6e6e6;
}
[contenteditable="true"]:empty:before {
content: attr(placeholder);
pointer-events: none;
display: block; /* For Firefox */
}
<div class="input-field" placeholder="Tell me something..." contentEditable="true"></div>
When the button is pressed it generates random numbers, that goes into the input box. Now, once the input box overflows, I have set the focus, so that it is visible what is going into the input box.
Now the problem is, observe after there's overflow, if you keep the button pressed the focus comes back to left and when you release it, the focus goes back to right, which looks very disturbing, I just want to fix that. I hope what I am trying to say is clear.
const input = document.querySelector('input');
const button = document.querySelector('button');
button.addEventListener('click', function() {
// concatenate random number to input value
input.value += Math.floor(Math.random() * 10)
// focus input to scroll to end
input.focus();
});
input {
width: 5rem;
}
<input type='text' readonly='true' value='0'>
<button>Click</button>
Simply add direction: rtl; in your input CSS.
const input = document.querySelector('input');
const button = document.querySelector('button');
button.addEventListener('click', function() {
input.value += Math.floor(Math.random() * 10)
});
html, body {
background: black;
}
input {
width: 15rem;
height: 3rem;
margin: 2rem .4rem 2rem .4rem;
font-size: 3rem;
text-align: right;
color: white;
background-color: black;
outline: none;
transition: all .1s ease;
direction: rtl;
}
<input type='text' readonly='true' value='0'>
<button>CLICK ME</button>
UPDATED
According to your last link https://codepen.io/ssmkhrj/pen/ExPjJab
This you should change in your code
<div class="display-cover">
<div class="display"></div>
</div>
and CSS
.display-cover{
width: 19.2rem;
height: 3rem;
margin: 0 .4rem 1.5rem .4rem;
text-align: right;
font-size: 3rem;
white-space: nowrap;
padding-bottom: 0.5rem;
overflow-y: hidden;
overflow-x: scroll;
scroll-snap-type: x mandatory; /* this will do the magic for parent */
}
.display{
height: 3rem;
display: inline-block;
scroll-snap-align: end; /* this will do the magic for child */
}
More about this scroll-snap here https://css-tricks.com/practical-css-scroll-snapping/
Add dir="rtl" attribute to input field, it will flow text from right to left. W3Schools
const input = document.querySelector('input');
const button = document.querySelector('button');
button.addEventListener('click', function() {
// concatonate random number to input value
input.value += Math.floor(Math.random() * 10)
// focus input to scroll to end
input.focus();
});
html, body {
background: black;
}
input {
width: 15rem;
height: 3rem;
margin: 2rem .4rem 2rem .4rem;
font-size: 3rem;
text-align: right;
color: white;
background-color: black;
outline: none;
}
<input type='text' readonly='true' value='0' dir="rtl">
<button>CLICK ME</button>
Using setInterval makes the input stay focused so it doesn't jump back and forth. It still is a little buggy though when you click outside of the input or button elements, but this gets the job done for the jumping.
const input = document.querySelector('input');
const button = document.querySelector('button');
button.addEventListener('click', function() {
// concatonate random number to input value
input.value += Math.floor(Math.random() * 10)
});
setInterval(function(){
input.focus();
});
html, body {
background: black;
}
input {
width: 15rem;
height: 3rem;
margin: 2rem .4rem 2rem .4rem;
font-size: 3rem;
text-align: right;
color: white;
background-color: black;
outline: none;
}
<input type='text' readonly='true' value='0' onfocus="this.value = this.value;" autofocus>
<button>CLICK ME</button>
i want to no repeat the tags that i will writing in field i try but the console appears the span value repeat like this in image how can i fix that to not repeat the tags. i used JQuery
s://i.stack.imgur.com/dIVP1.jpg
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<input class="add-tags" type="text" placeholder="Type Your Tags">
<div class="tags"></div>
</div>
<script>
$('.add-tags').on('keyup', function(e) {
var tagsKey = e.keyCode || e.which;
if (tagsKey === 188) {
var thisValue = $(this).val().slice(0, -1); //remove last letter
$('.tags').append('<span class="tags-span"><i class="fa fa-times"></i>' + thisValue + '</span>');
var spanvalue = $('.tags-span').text();
console.log(spanvalue);
if (thisValue === spanvalue) {
console.log('good');
} else {
console.log('bad');
}
$(this).val('');
}
$('.tags').on('click', '.tags-span i', function() {
$(this).parent('.tags-span').remove();
});
});
</script>
Voila!
I have a gift for you, but first I would like to point out, that next time you should invest more time into the preparation of your question. Don't cry, don't beg, start from doing your homework first and get as much information as you can. Stackoverflow is not a place were people will do you job for you.
Right now, one can only guess what you are really trying to achieve.
After some harsh treatment let's go to the good parts:
In the following example (https://jsfiddle.net/mkbctrlll/xb6ar2n1/95/)
I have made a simple input that creates a tag on a SPACE key hit. Each tag could be easily removable if X is clicked.
HTML:
<form class="wrapper">
<label for="test">
<span id="tags">
Tags:
</span>
<input id="test" type="text" >
</label>
</form>
JS:
var tagsCollection = []
document.body.onkeyup = function(e){
if(e.keyCode == 32){
var content = getContent('#test')
console.log(tagsCollection.indexOf(content))
if(tagsCollection.indexOf(content) === -1) {
console.log('Spacebar hit! Creating tag')
createTag(content)
tagsCollection.push(content)
console.log(tagsCollection)
} else {
console.log('We already have this one sir!')
displayError('Whoops! Looks like this tag already exists... )')
}
}
}
$('.wrapper').on('click', function(event) {
$(event.target).closest('.tag').remove()
})
function displayError(content) {
var error = document.createElement('p')
error.classList.add('error')
error.innerHTML = content
document.querySelector('.wrapper').append(error)
}
function getContent(target) {
var value = $(target).val().replace(/\W/g, '')
$(target).val("")
return value
}
function createTag(content) {
var $tags = $('#tags')
var tag = document.createElement('span')
var closeIcon = '×'
var iconHTML = document.createElement('span')
iconHTML.classList.add('remove')
iconHTML.innerHTML = closeIcon
tag.classList.add('tag')
tag.append(iconHTML)
tag.append(content)
$tags.append(tag)
}
function removeTag(target) {
target.remove()
}
CSS:
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
.wrapper {
background: #fff;
border-radius: 4px;
padding: 20px;
font-size: 25px;
transition: all 0.2s;
margin: 0 auto;
width: 300px;
}
#tags {
display: block;
margin-bottom: 10px;
font-size: 14px;
}
#test {
display: block;
width: 100%;
}
.tag {
border-radius: 16px;
background-color: #ccc;
color: white;
font-size: 12px;
padding: 4px 6px 4px 16px;
position: relative;
}
.tag:not(:last-child) {
margin-right: 4px;
}
.remove {
font-weight: 600;
position: absolute;
top: 50%;
left: 6px;
cursor: pointer;
transform: translateY(-50%);
}
.remove:hover {
color: red;
}
It is just a quick and dirty example, not a production lvl code.
Here is what I try to acomplish: I need an input field containing a value with a unit, that would look like this:
On focussing the input, I want it to move the unit to the right side, looking like this:
I can think of two ways to do so:
1. Replace input field with a Div that looks exactly like the input when focus is lost, and set the value of the input as its content:
$('#fakeInput').bind('click', changeToRealInput);
$('#realInput').bind('blur', changeToFakeInput);
$('#realInput').trigger('blur');
$('#unitAddon').html($('#realInput').attr('unit'));
function changeToFakeInput() {
// hide actual input and show a div with its contents instead
$('#fakeInput').show();
$('#realInputContainer').hide();
$('#fakeInput').html($('#realInput').val() + $('#realInput').attr('unit'));
}
function changeToRealInput() {
// hide fake-div and set the actual input active
$('#fakeInput').hide();
$('#realInputContainer').show();
$('#realInput').focus();
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
div#container {
display: flex;
background: #8aaac7;
padding: 10px;
width: 200px;
}
div#unitAddon,
input#realInput,
div#fakeInput {
font-family: sans-serif;
font-size: 26px;
padding: 5px;
width: 100%;
background-color: #FFFFFF;
border: none;
outline: none;
}
div#realInputContainer,
div#fakeInput {
border: 2px solid #dadada;
}
div#realInputContainer {
display: flex;
}
div#unitAddon {
width: auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div id="container">
<div id="fakeInput"></div>
<div id="realInputContainer">
<input type="number" unit="kg" id="realInput" value="3.3">
<div id="unitAddon"></div>
</div>
</div>
(also see this jsFiddle)
Problem here is (as you can see in the screenshot above) that, depending on your local settings, chrome automatically converts the decimal point into a comma (in the input, but not in the fake-div)
Another way I thought of is: When the focus is lost, set the size of the input field to match its content and, by doing so, pull the addon displaying the unit just behind the number.
Problem here is to get the size of the content of an input (cross-browser):
$('#realInput').bind('focus', changeToRealInput);
$('#realInput').bind('blur', changeToFakeInput);
$('#realInput').trigger('blur');
$('#unitAddon').html($('#realInput').attr('unit'));
function changeToFakeInput() {
// here is the question: what width should it be?
$('#realInput').css({'width' : '40%'});
}
function changeToRealInput() {
$('#unitAddon').css({'width' : 'auto'});
$('#realInput').css({'width' : '100%'});
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
div#container {
display: flex;
background: #8aaac7;
padding: 10px;
width: 300px;
}
div#unitAddon,
input#realInput{
font-family: sans-serif;
font-size: 26px;
padding: 5px;
width: 100%;
border: none;
outline: none;
}
div#realInputContainer {
border: 2px solid #dadada;
display: flex;
background-color: #FFFFFF;
}
div#realInputContainer.setAddonAway > div#unitAddon {
width: auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div id="container">
<div id="realInputContainer" class="setAddonClose">
<input type="number" unit="kg" id="realInput" value="3.3">
<div id="unitAddon"></div>
</div>
</div>
also see this jsFiddle
I could accomlish this with an input[type=text], but I dont want to loose the benefits of type[number] (min/max/step validation, on-screen keyboard, etc.)
Is there any way of getting around the flaws of my two ideas? Or is thre a more elegant way to do so?
The idea is to: (1) make the input box to cover the entire container; (2) create a helper element, and set it the same length as the input value via JS, and make it invisible as a place holder; (3) apply some style for moving around the unit box.
codepen
$(document).ready(function() {
$(".value").text($(".number").val());
$(".unit").text($(".number").attr("unit"));
$(".number").on("change keypress input", function() {
$(".value").text($(".number").val());
});
});
* {
box-sizing: border-box;
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
.container {
position: relative;
display: flex;
border: 4px solid teal;
width: 200px;
}
.container > * {
font-family: sans-serif;
font-size: 20px;
}
.number {
position: absolute;
left: 0;
top: 0;
width: 100%;
padding: 0;
border: 0;
outline: 0;
background: transparent;
}
.value {
visibility: hidden;
max-width: 100%;
overflow: hidden;
}
.unit {
position: relative;
flex: 1;
pointer-events: none;
background: white;
}
.number:focus ~ .value {
flex: 1;
}
.number:focus ~ .unit {
flex: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<input class="number" type="number" value="1.23" unit="kg">
<span class="value"></span>
<span class="unit"></span>
</div>