Grow and shrink the input - javascript

I am looking for a way to control the size of my input element in the way similar to divs.
.main {
width: 100%;
display: flex;
}
.line {
min-width: 2rem;
display: flex;
}
.input {
flex: 1 1 auto;
min-width: 0;
}
<div class="main">
<div class="line">
<input class="input" />
</div>
</div>
Here I am expecting that when the view appears the initial input is of size 2rem (padding, margin, border do not matter here) and when you start typing and exceeds the size it grows till it reach the size of main. Any suggestions how I can achieve that?

An example using only CSS and the contenteditable attribute:
.main {
display: flex;
}
.line {
display: flex;
}
.input {
border: 1px solid lightslategray;
padding: 0.5em;
margin: 1em;
min-width: 30ch;
max-width: 100%;
}
<div class="main">
<div class="line">
<p class="input" contenteditable></p>
</div>
</div>

Here is another solution using the .getBoundingClientRect(), so the idea is to add the text of the input to a p element calculate the width of it then set it as the width of the input, this keeps using the same input instead of content editable trick that a user can press enter and have multiple lines, also can be used in forms...
var txtElement = document.querySelector("#hidden-text");
document.querySelector(".input").oninput = function() {
txtElement.innerHTML = this.value;
var width = txtElement.getBoundingClientRect().width;
this.style.width = width < 100 ? 100 : width + "px";
};
.input {
width: 100px;
}
#hidden-text {
display: inline-block;
position: absolute;
visibility: hidden;
}
#hidden-text, .input {
font-size: 16px;
font-family: Serif;
}
<div class="main">
<div class="line">
<input class="input"/>
</div>
</div>
<p id="hidden-text"></p>

Probably not the most efficient solution, but you could do something like this:
document.querySelector('.input').addEventListener('input', function(e) {
let span = document.createElement('span');
span.innerHTML = e.target.value;
span.style.visibility = 'hidden';
span.style.fontSize = '15px';
document.body.appendChild(span);
if (span.offsetWidth > e.target.offsetWidth - 10) {
e.target.style.width = span.offsetWidth + 'px';
}
span.parentNode.removeChild(span);
});
.main {
width: 100%;
display: flex;
}
.line {
min-width: 2rem;
display: flex;
}
.input{
flex: 1 1 auto;
min-width: 0;
}
<div class="main">
<div class="line">
<input class="input"/>
</div>
</div>

Related

How do I give elements with the same class name to perform the same function in javascript?

I have multiple elements with the same class name and want them to perform the same javascript function but output their unique "inner.Text" specific
to the element i clicked. Right Now i know i need an [index] and a loop
but I just don't know how to implement that at the moment since im a novice in javascript.
spent 3 hours trying to figure it out
const myButton = document.querySelectorAll('.clipboard-icon');
const myInp = document.querySelectorAll('.snippetcode');
myButton.forEach(ree =>
ree.addEventListener('click', copyElementText));
function copyElementText(id) {
var text = myInp.innerText;
var elem = document.createElement("textarea");
document.body.appendChild(elem);
elem.value = text;
elem.select();
document.execCommand("copy");
document.body.removeChild(elem);
console.log('clicked');
}
console.log(myButton);
console.log(myInp);
/*everything works fine if the script was changed to affect only ONE class name but I cant figure out how to make them work for more than one
*/
.snippet1 {
border: solid rgb(55, 63, 184) 3px;
}
.snippet_holder {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100%;
padding: 0 3.5rem;
margin: 0 0 1rem 0;
position: relative;
}
.buttons {
width: 170px;
height: 40px;
border: 0;
padding: 0;
font-size: 1rem;
color: #fff;
border-radius: 10px;
}
.snippet_holder:hover .clipboard-icon {
display: block;
position: absolute;
top: 15px;
right: 65px;
background: rgb(51, 153, 136);
margin: 0;
width: 30px;
padding: 0;
height: 30px;
}
.clipboard-icon {
display: none;
}
.clipboard-icon img {
width: inherit;
height: inherit;
position: relative;
}
.clipboard-icon pre {
display: none;
}
.snippetcode1 {
display: none;
}
.button1 {
-webkit-animation: rainbow 6.5s ease infinite;
animation: rainbow 6.5s ease infinite;
background-image: linear-gradient(124deg, #ff470f, #ff3860, #b86bff, #3273dc);
background-size: 800% 800%;
}
#keyframes rainbow {
0% {
background-position: 1% 80%;
}
50% {
background-position: 99% 20%;
}
100% {
background-position: 1% 80%;
}
0% {
background-position: 1% 80%;
}
50% {
background-position: 99% 20%;
}
100% {
background-position: 1% 80%;
}
}
main {
margin: 0 auto;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
<html>
<body>
<main>
<div class="snippet_holder snippet_holder1">
<div class="clipboard-icon">
<pre>
<code class= "snippetcode">
1st class text copieddddd
</code>
</pre>
<img src="https://www.shareicon.net/data/128x128/2016/04/27/756265_clipboard_512x512.png">
</div>
<button type="button" class="buttons button1">button</button>
</div>
<div class="snippet_holder snippet_holder1">
<div class="clipboard-icon">
<pre>
<code class= "snippetcode">
22222nddd class text copieddddd
</code>
</pre>
<img src="https://www.shareicon.net/data/128x128/2016/04/27/756265_clipboard_512x512.png">
</div>
<button type="button" class="buttons button1">button</button>
</div>
<div class="snippet_holder snippet_holder1">
<div class="clipboard-icon">
<pre>
<code class= "snippetcode">
3rd class text copieddddd
</code>
</pre>
<img src="https://www.shareicon.net/data/128x128/2016/04/27/756265_clipboard_512x512.png">
</div>
<button type="button" class="buttons button1">button</button>
</div>
<div class="snippet_holder snippet_holder1">
<div class="clipboard-icon">
<pre>
<code class= "snippetcode">
4thhhhhhhhclass text copieddddd
</code>
</pre>
<img src="https://www.shareicon.net/data/128x128/2016/04/27/756265_clipboard_512x512.png">
</div>
<button type="button" class="buttons button1">button</button>
</div>
</main>
</body>
</html>
You've to query the .snippetcode related to the button pressed, so, it makes no sense to query the nodeList myInp, you can access to the right element using the currentTarget provided in the event object...
function copyElementText(event) {
var text = event.currentTarget.querySelector('.snippetcode').innerText;
var elem = document.createElement("textarea");
document.body.appendChild(elem);
elem.value = text;
elem.select();
document.execCommand("copy");
document.body.removeChild(elem);
console.log('clicked');
}

How to skip an element from array in addEventListener javascript?

i have a problem with my output. What i am trying to do is make every tag element with a class name of "editable" have an event listener of "click" and when you click on that element a slidebar appears for optional edit. When i click on any of the elements and edit the innerHTML text it works fine, the problem is when i click on that second element and try to edit that other one both of the elements change. What would i do to prevent the first element to not change?
Thank you in advance!
var elements = document.querySelectorAll(".editable");
var sidebar = document.querySelector(".sidebar");
var content = document.querySelector(".content");
elements.forEach(function(element){
element.addEventListener('click', function(e){
sidebar.style.display = "block";
content.style.marginLeft = "300px";
const form = document.forms['change-text'];
form.addEventListener('submit', function(eve){
eve.preventDefault();
const value = form.querySelector('input[type="text"]').value;
element.innerHTML = value;
});
},true);
});
html,body{
margin: 0;
}
.editable:hover{
border: 1px dashed #ccc;
}
.sidebar {
position: fixed;
width: 300px;
height: 100%;
background: #ccc;
padding: 20px;
box-sizing: border-box;
display: none;
transition: 1s;
}
.content {
margin-left: 0px;
height: auto;
width: auto;
position: relative;
overflow: auto;
z-index: 1;
padding: 20px;
box-sizing: border-box;
}
.info {
width: auto;
height: auto;
position: relative;
}
<body>
<div class="sidebar">
<p>Enter text here:</p>
<form id="change-text">
<input type="text" placeholder="Change text">
<button>Save</button>
</form>
</div>
<div class="content">
<div class="info">
<h1 class="editable">Title</h1>
<p class="editable">Subtitle</p>
</div>
</div>
</body>
There is
element.innerHTML = value;
in the loop. Both <h1> and <p> have the same class name .editable. If you change <p>, it also affects the <h1>.
Give them both an unique id and separate the loop and form-submit. Store the current clicked id. Access it when the form is submited.
var elements = document.querySelectorAll(".editable");
var sidebar = document.querySelector(".sidebar");
var content = document.querySelector(".content");
var current;
elements.forEach(function(element, i) {
element.addEventListener('click', function(e) {
sidebar.style.display = "block";
content.style.marginLeft = "300px";
current = this.id; // Current id
});
});
const form = document.forms['change-text'];
form.addEventListener('submit', function(eve) {
eve.preventDefault();
const value = form.querySelector('input[type="text"]').value;
document.getElementById(current).innerHTML = value; // Assign the input value to the current element
});
html,
body {
margin: 0;
}
.editable:hover {
border: 1px dashed #ccc;
}
.sidebar {
position: fixed;
width: 300px;
height: 100%;
background: #ccc;
padding: 20px;
box-sizing: border-box;
display: none;
transition: 1s;
}
.content {
margin-left: 0px;
height: auto;
width: auto;
position: relative;
overflow: auto;
z-index: 1;
padding: 20px;
box-sizing: border-box;
}
.info {
width: auto;
height: auto;
position: relative;
}
<body>
<div class="sidebar">
<p>Enter text here:</p>
<form id="change-text">
<input type="text" placeholder="Change text">
<button>Save</button>
</form>
</div>
<div class="content">
<div class="info">
<h1 id="title" class="editable">Title</h1>
<p id="subtitle" class="editable">Subtitle</p>
</div>
</div>
</body>

Append canvas with parent's height creates scrollbars

I have problem with canvas tag. I need to append it into parent div. But when I set precise dimensions of parent and embed canvas tag, I get scrollbars. When I do same think with div, it works good. Here is fiddle:
https://jsfiddle.net/57yovrkx/4/
and here is code:
$(function() {
var content1 = $('#content1');
var div = $('<div/>', {width: content1[0].clientWidth, height: content1[0].clientHeight});
content1.append(div);
var content2 = $('#content2');
var canvas = $('<canvas/>', {width: content2[0].clientWidth, height: content2[0].clientHeight});
content2.append(canvas);
})
* {
box-sizing: border-box;
border: 0;
margin: 0;
padding: 0;
}
#wrap1, #wrap2 {
display: flex;
flex-direction: column;
position: absolute;
left: 0;
right: 0;
overflow: auto;
}
#wrap1 {
top: 0;
bottom: 50%;
}
#wrap2 {
top: 50%;
bottom: 0;
}
.header {
flex: 0 0 2rem;
background: darkgrey;
}
#content1, #content2 {
flex: 1;
}
#content1 {
background: lightblue;
}
#content2 {
background: lightgreen;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="wrap1">
<div class="header">
Some header
</div>
<div id="content1">
</div>
</div>
<div id="wrap2">
<div class="header">
Some header
</div>
<div id="content2">
</div>
</div>
Does anybody knows why?
Setting canvas.style.height = '100%'; before you append the canvas seems to do the trick.
https://stackoverflow.com/a/10215724/1482623

Adjust inline-flex elements to the screen width

In the image below you can see a div (red background) with three divs (green background) inside and each of them with a label (blue background) and a control (input or checkbox) inside.
In the image you can see how the green divs adjust to the size of the screen and when they reach their minimum size and no longer fit in the line they move down, the problem is (as you can see in the third windows) it looks awful when they are in different lines because of their minimum size attributes, what I want to do is to make them occupy the 100% of the screen when they move down.
I hope I explained correctly.
By the way, I don't want to use plugins, so if there is a way of doing this with only HTML, CSS and Javascript I would be very happy.
Thank you so much by advance.
Here is my code:
.controlRow
{
display: flex;
flex-wrap: wrap;
padding: 5px 0px;
margin: 0px;
background-color: Red;
}
.controlColumn1-2, .controlColumn1-4, .controlColumnCheckbox
{
display: inline-flex;
background-color: green;
}
.controlColumn1-2 { width: 50%; min-width: 300px; }
.controlColumn1-4 { width: 25%; min-width: 300px; }
.controlColumnCheckbox { width: 230px; }
.controlColumn1-2 label, .controlColumn1-4 label, .controlColumnCheckbox label
{
margin-right: 5px;
min-width: 100px;
background-color: blue;
}
.controlColumn1-2 input, .controlColumn1-4 input
{
margin-right: 5px;
width: 100%;
}
.controlColumnCheckbox input
{
margin-left: 105px;
margin-right: 5px;
width: 14px;
}
<div class = "controlRow">
<div class = "controlColumn1-4">
<label id="lblNombre">Nombre:</label>
<input type="text" id="txtNombre" maxlength="100" />
</div>
<div class = "controlColumn1-2">
<label id="lblDescripcion">Descripcion:</label>
<input type="text" id="txtDescripcion" maxlength="100" />
</div>
<div class = "controlColumn1-4">
<div class = "controlColumnCheckbox">
<input type="checkbox" id = "checkActivo" checked="checked" />
<label id="lblActivo">Activo.</label>
</div>
</div>
</div>
Instead of this:
.controlColumn1-2 { width: 50%; min-width: 300px; }
.controlColumn1-4 { width: 25%; min-width: 300px; }
Try this:
.controlColumn1-2 { flex: 2 0 300px; }
.controlColumn1-4 { flex: 1 0 300px; }
DEMO
Learn more about the flex property at MDN.
Also a great reference: Common Values of Flex

Dot leaders with picture background

So I'm building a website for a restaurant and I'm in a pickle. I'm trying to create the menu there. The idea is to align the food name to the left, the price to the right and fill the gap between them with dots. Like this
Hamburger ............................................ $ 4.00
XXL Hamburger .................................... $ 4.00
Milkshake .............................................. $ 4.00
I found a couple of solutions, which only work if you have a background with one color and no texture. The idea was to fill the whole line with dots and set the name/price background span with the same color as the site background, so the dots wouldn't show. But I have a picture for the background.
I'm not going to post my code here, because it wouldn't really matter or help.
Is it even possible? Doesn't have to be css only, might as well be done with JavaScript.
I am kinda late, but you can quite easily do it with a radial-gradient:
.col {
display: inline-block;
vertical-align: top;
}
.names span {
width: 200px;
display: flex;
}
.prices span {
display: block;
text-align:right;
}
.names span:after {
content: "";
display: inline-block;
height: 1em;
flex-grow: 1;
background: radial-gradient(black 25%, transparent 25%) scroll repeat-x bottom left/5px 5px;
}
<div class='names col'>
<span>Hamburger</span>
<span>Hot Dogs</span>
<span>Superman Hamburger</span>
</div>
<div class='prices col'>
<span>$1.00</span>
<span>$0.50</span>
<span>$400.00</span>
</div>
JSFiddle Demo
It's easy to do with some simple javascript and css, here's a fiddle: jsfiddle
The key is to set the width of the div that holds the dots to the width of the column minus the width of the food name minus the width of the price, and to make sure there are more than enough dots to cover the distance, and to set overflow: hidden for the dot div.
$(".menu-row").each(function(index, element) {
var menuRowWidth = $(element).width();
var foodItemWidth = $(element).children('.food-item').width();
var priceWidth = $(element).children('.price').width();
var $dotFiller = $(element).children('.dot-filler');
var dotFillerWidth = menuRowWidth - foodItemWidth - priceWidth;
$dotFiller.width(dotFillerWidth + "px");
});
Then float the item and dot div left, the price right, all within a set width column. It's also important that overflow: hidden is set for the dots, because when we set the width of that div in javascript we want all extra dots to just be cut off. The CSS:
.food-item {
float: left
}
.dot-filler {
overflow: hidden;
width: 0;
float: left;
}
.price {
float: right;
}
.menu-row {
width: 400px;
}
Then structure your html as follows:
<div class="menu-row">
<div class="food-item">Steak</div>
<div class="dot-filler">............................................................................................</div>
<div class="price">$18.00</div>
</div>
<div class="menu-row">
<div class="food-item">Hamburger</div>
<div class="dot-filler">............................................................................................</div>
<div class="price">$8.00</div>
</div>
You can use a wrapper to set a fix width of your Name + Dots.
The css will look like this:
.wrapper {
width: 300px;
overflow: hidden;
display: inline-block;;
white-space: nowrap;
}
The HTML like this:
<div>
<ul class="noDisc">
<li>
<div class="wrapper">
<span>HAMBURGER </span>
<span>...............................................................</span>
</div>
<span>$ 40.00</span>
</li>
<li>
<div class="wrapper">
<span>FRIED CHIKEN </span>
<span>...............................................................</span>
</div>
<span>$ 13.00</span>
</li>
<li>
<div class="wrapper">
<span>STEAK ON A STICK </span>
<span>...............................................................</span>
</div>
<span>$ 99.00</span>
</li>
</ul>
</div>
Live sample:
fiddle
Use display:table; and display: table-cell; for the divs inside the list-elements and border-bottom: Xpx dotted black; for the dots.
ul{
margin: 0;
padding: 0;
}
ul li{
display: table;
width: 100%;
}
ul li div {
display: table-cell;
}
ul li div.food {
padding-right: 5px;
}
ul li div.dots {
border-bottom: 1px dotted #000;
width: 100%;
position: relative;
top: -4px;
}
ul li div.price {
padding-left: 5px;
}
<ul>
<li>
<div class="food">Spaghetti</div>
<div class="dots"> </div>
<div class="price">10.00$</div>
</li>
<li>
<div class="food">Spaghetti</div>
<div class="dots"></div>
<div class="price">10.00$</div>
</li>
<li>
<div class="food">Spaghetti</div>
<div class="dots"></div>
<div class="price">10.00$</div>
</li>
</ul>
Thanks. I used what you had here and improved on it. This code is meant for woocommerce product items, but can be edited for whatever you need. $containerElement is the element you are measuring the width of.
/**
* dotFiller
* adds dynamic dot leaders between product title and count element (<mark>)
* #return void
*/
var dotFiller = function(){
var $containerElement = $('ul.products li.product.has-children h2'),
df = '<div class="df">.....................................................................</div>';
$containerElement.each(function(i,el){
var $el = $(el),
w = $el.width(),
mw = $el.find('mark').width(),
tw = $el.find('span').width(),
dfw = (w - mw - tw) - 24;
// if its not there, lets add it
if (!$(el).has('.df').length){
$el.find('span').after(df);
}
$el.find('.df').css('width',dfw + "px");
});
};
dotFiller();
With this code, you can update/ recalculate on resize like so :
$('window').on('resize',function(){ dotFiller(); });
And here is my css for the internal elements:
mark {
background-color: transparent;
color: $secondary;
display: inline-block; float: right;
font-weight: normal;
}
div.df {
overflow: hidden;
display: inline-block;
margin-left: 10px;
position: relative;
top: 2px;
font-weight: normal;
opacity: 0.8;
}
I hope this helps someone!
Use a div that has a flex spacer with a border-bottom to achieve easy leader dots... The flex layout seems to be the most elegant solution. No pseudo-elements, or left and right block display, etc... Very simple...
HTML
<div class="list-item">
<div class="header-row">
<h4>Menu</h4>
</div>
<br>
<div class="list-item-row">
<div class="left">Hamburger</div>
<div class="dots"></div>
<div class="right">$5.00</div>
</div>
<div class="list-item-row">
<div class="left">Hamburger (XXL)</div>
<div class="dots"></div>
<div class="right">$7.50</div>
</div>
<div class="list-item-row">
<div class="left">Milkshake</div>
<div class="dots"></div>
<div class="right">$3.50</div>
</div>
<div class="list-item-row">
<div class="left">Pickle</div>
<div class="dots"></div>
<div class="right">Free</div>
</div>
</div>
CSS
#import url("https://fonts.googleapis.com/css?family=Lato|Montserrat:400,700|Roboto:400,700");
* {
margin: 0;
padding: 0;
}
button {
font-family: "Roboto";
font-size: 16px;
padding: 5px;
border-radius: 3px;
border: solid #424242 1px;
}
.list-item {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
margin: 20px;
font-family: "Lato";
background: #f0f0f0;
padding: 10px;
border: solid #e0e0e0 1px;
}
.list-item-row,
.header-row {
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
.left,
.right {
font-family: "Roboto";
}
.right {
color: blue;
}
}
.dots {
flex: 1 0 0;
border-bottom: dotted 2px #b0b0b0;
margin-left: 1em;
margin-right: 1em;
}
See Codepen here => https://codepen.io/anon/pen/vVZmxB

Categories