I am working on a meme generator project, it's to practice DOM manipulation. So, I'm supposed to use vanilla JavaScript and no canvas. I'm having trouble finding answer with these parameters. I am almost done with the project, I just need to add text to the picture that they submitted.
I have tried using innerText and innerHTML they seem to just replace it. I've tried append child, similar to how I got the appendchild with the image, but I either get an error or replace the image.
I was pretty sure that I needed to add it add the picture with JavaScript then style it with CSS. Maybe just add a class with classList.
console.log('Currentfile: memegenerator');
let img = document.getElementsByTagName('img');
addEventListener('submit', function(e) {
e.preventDefault();
let UIurl = document.getElementById('picurl');
let memeToBe = UIurl.value;
let img = document.createElement('img');
img.setAttribute('src', memeToBe);
img.setAttribute('class', 'meme');
// append to the document with set attribute using said variable
let memeLocation = document.getElementById('location');
memeLocation.appendChild(img);
//url for pic test
//https://www.fillmurray.com/640/360
//add text to image
//get text values
let inputText = document.getElementById('text_top');
let textValue = inputText.value;
addEventListener('click', function(e) {
let clickedElement = e.target;
console.log(clickedElement);
let targetCheck = clickedElement.classList.contains("meme");
if (targetCheck) {
clickedElement.remove();
}
})
h1 {
color: navy;
}
.center {
text-align: center;
}
.main {
width: 50%;
margin: 0 auto;
background-color: lightblue;
border-radius: 0.5rem;
border: 0.08rem solid black;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
transition: 0.3s;
}
.meme {
width: 99%;
/* margin: 2 auto; */
justify-content: center;
background-color: lightblue;
border-radius: 0.5rem;
border: 0.08rem solid black;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
transition: 0.3s;
}
main:hover {
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2);
}
meme:hover {
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2);
}
body {
background-color: #f0feff;
}
.button {
float: right;
}
/* divider styles */
hr.rounded {
border-top: 2px solid #bbb;
border-radius: 5px;
width: 90%;
}
.border_lower {
border-bottom: 1px solid #bbb;
border-radius: 5px;
width: 90%;
}
/* form styles */
form {
width: 60%;
margin: 0 auto;
}
form input {
margin: 2px;
}
form label {
margin: 2px;
}
/* Container holding the image and the text */
.container {
position: relative;
text-align: center;
color: white;
}
/* Bottom left text */
.bottom-left {
position: absolute;
bottom: 8px;
left: 16px;
}
/* Top left text */
.location {
position: absolute;
top: 8px;
left: 16px;
}
<main class="main">
<h1 class="center">MEME GENERATOR!</h1>
<hr class="rounded" />
<form action="#" class="form">
<label for="text_top">Text Upper:</label>
<input type="text" name="text_top" id="text_top" /><br />
<label for="text_lower">Text Lower:</label>
<input type="text" name="text_lower" id="text_lower" /><br />
<label for="picturl">Picture:</label>
<input type="url" name="picurl" id="picurl" /><br /><br /><br />
<input class="button " type="submit" value="Add Meme:" /><br />
<hr />
</form>
<div id="location"></div>
<hr class="border_lower" />
<p class="center"><small>Thanks for visiting!</small></p>
</main>
Since this isn't about saving the images but just for display purposes, I got it working.
The main problem that you were having is your approach seemed to focus more on the javascript side of things but missed out on the CSS part of it.
There are multiple ways to put images behind text, the most common two are:
Setting the images as a background image on the parent element (ie div) then just setting the text within that element
Using CSS to absolute position the text on the image and use z-index to layer them
For my answer I chose #2.
Besides misc code clean ups, the main function that I did was:
I created a div, and gave it a class of meme
I added the image to that div
I added the top and bottom text to their own divs and append those to the meme div
Using CSS, I positioned the top and bottom text above the image
A few other things, when adding an eventListeners unless it is absolutely needed, I recommend tying them to a specific element and not just the document (or nothing at all which I believe is document anyway). By applying it to the document, any click will be processed, but by tying it to the element, only clicks on that element will be processed.
console.log('Currentfile: memegenerator');
let img = document.getElementsByTagName('img');
let form = document.querySelector('form');
form.addEventListener('submit', function(e) {
e.preventDefault();
let meme = document.createElement("div");
let top_text = document.createElement("div");
let bottom_text = document.createElement("div");
let img = document.createElement("img");
let btn = document.createElement("button");
btn.setAttribute("type","button");
img.src = document.getElementById('picurl').value;
top_text.classList.add("top_text");
top_text.innerHTML = document.getElementById('text_top').value;
bottom_text.classList.add("bottom_text");
bottom_text.innerHTML = document.getElementById('text_lower').value;
btn.innerHTML = "REMOVE";
meme.classList.add("meme");
meme.appendChild(top_text);
meme.appendChild(bottom_text);
meme.appendChild(img);
meme.appendChild(btn);
let memeLocation = document.getElementById('location');
memeLocation.appendChild(meme);
document.getElementById('picurl').value = "";
document.getElementById('text_top').value = "";
document.getElementById('text_lower').value = "";
btn.addEventListener('click', function(e) {
meme.remove();
})
});
h1 {
color: navy;
}
.center {
text-align: center;
}
.main {
width: 50%;
margin: 0 auto;
background-color: lightblue;
border-radius: 0.5rem;
border: 0.08rem solid black;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
transition: 0.3s;
}
.meme {
width: 99%;
/* margin: 2 auto; */
justify-content: center;
border-radius: 0.5rem;
border: 0.08rem solid black;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
transition: 0.3s;
position:relative;
}
.top_text{
position:absolute;
top:10px;
left:30px;
color:#ffffff;
z-index:3
}
.bottom_text{
position:absolute;
bottom:20px;
left:30px;
color:#ffffff;
z-index:3
}
.meme img{
max-width:100%;
z-index:2
}
main:hover {
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2);
}
meme:hover {
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2);
}
body {
background-color: #f0feff;
}
.button {
float: right;
}
/* divider styles */
hr.rounded {
border-top: 2px solid #bbb;
border-radius: 5px;
width: 90%;
}
.border_lower {
border-bottom: 1px solid #bbb;
border-radius: 5px;
width: 90%;
}
/* form styles */
form {
width: 60%;
margin: 0 auto;
}
form input {
margin: 2px;
}
form label {
margin: 2px;
}
/* Container holding the image and the text */
.container {
position: relative;
text-align: center;
color: white;
}
/* Bottom left text */
.bottom-left {
position: absolute;
bottom: 8px;
left: 16px;
}
/* Top left text */
.location {
position: absolute;
top: 8px;
left: 16px;
}
<main class="main">
<h1 class="center">MEME GENERATOR!</h1>
<hr class="rounded" />
<form action="#" class="form">
<label for="text_top">Text Upper:</label>
<input type="text" name="text_top" id="text_top" /><br />
<label for="text_lower">Text Lower:</label>
<input type="text" name="text_lower" id="text_lower" /><br />
<label for="picturl">Picture:</label>
<input type="url" name="picurl" value="https://www.fillmurray.com/640/360" id="picurl" /><br /><br /><br />
<input type="submit" value="Add Meme:" /><br />
<hr />
</form>
<div id="location"></div>
<hr class="border_lower" />
<p class="center"><small>Thanks for visiting!</small></p>
</main>
Related
I am a JS beginner and I have the following problem: I want that as soon as someone clicks on the URL icon inside the accordion the respective link is copied to the clipboard. Unfortunately (always) only the first link is copied to the clipboard, even if one clicks on the other two URL icons only the first link is copied. Although in the clipboard should be link 2 (from the value field) when i click on URL icon 2 (and the same for number 3 of course). I hope I have described the problem clearly enough.
Where is the error and what do I need to change on the JS code to make it work? Thanks a lot for the help in advance!
```
<!DOCTYPE html>
<html>
<head>
<title>My example Website</title>
<style>
body {
font-size: 21px;
font-family: Tahoma, Geneva, sans-serif;
max-width: 550px;
margin: 0 auto;
background-color: black;
}
input {
display: none;
}
label {
display: block;
padding: 8px 22px;
margin: 0 0 1px 0;
cursor: pointer;
background: #181818;
border: 1px solid white;
border-radius: 5px;
color: #FFF;
position: relative;
}
label:hover {
background: white;
border: 1px solid white;
color:black;
}
label::after {
content: '+';
font-size: 22px;
font-weight: bold;
position: absolute;
right: 10px;
top: 2px;
}
input:checked + label::after {
content: '-';
right: 14px;
top: 3px;
}
.content {
background: #DBEECD;
background: -webkit-linear-gradient(bottom right, #DBEECD, #EBD1CD);
background: -moz-linear-gradient(bottom right, #DBEECD, #EBD1CD);
background: linear-gradient(to top left, #DBEECD, #EBD1CD);
padding: 10px 25px 10px 25px;
border: 1px solid #A7A7A7;
margin: 0 0 1px 0;
border-radius: 1px;
}
input + label + .content {
display: none;
}
input:checked + label + .content {
display: block;
}
.whitepaper {
cursor: pointer;
text-align: center;
background-color: white;
border: 2px solid black;
border-radius: 3px;
float: left;
margin: 5px 5px 5px 0;
height: 40px;
width: 30px;
}
.blackframe {
text-align: center;
background-color: black;
cursor: pointer;
font-family: Tahoma, Geneva, sans-serif;
font-size:12px;
font-weight:bold;
margin: 12px 0 12px 0;
color: white;
width: 30px;
}
.whitepaper:hover {
cursor: pointer;
text-align: center;
background-color: black;
border: 2px solid white;
border-radius: 3px;
float: left;
margin: 5px 5px 5px 0;
height: 40px;
width: 30px;
}
/* Tooltip container */
.tooltip {
position: relative;
display: inline-block;
}
/* Tooltip text */
.tooltip .tooltiptext {
visibility: hidden;
width: 120px;
background-color: #555;
color: #fff;
text-align: center;
padding: 5px 0;
border-radius: 6px;
/* Position the tooltip text */
position: absolute;
z-index: 1;
bottom: 125%;
left: 50%;
margin-left: -60px;
/* Fade in tooltip */
opacity: 0;
transition: opacity 0.3s;
}
/* Tooltip arrow */
.tooltip .tooltiptext::after {
content: "";
position: absolute;
top: 100%;
left: 50%;
margin-left: -5px;
border-width: 5px;
border-style: solid;
border-color: #555 transparent transparent transparent;
}
/* Show the tooltip text when you mouse over the tooltip container */
.tooltip:hover .tooltiptext {
visibility: visible;
opacity: 1;
}
</style>
</head>
<body>
<input type="checkbox" id="title1" name="contentbox" />
<label for="title1">Content 1</label>
<div class="content">
<div class="tooltip"><div class="whitepaper" onclick="myFunction()"><div class="blackframe"><span class="tooltiptext">Copy link 1 to clipboard</span>URL</div></div><input type="text" value="https://mywebsite.com/#title1" id="myInput"></div>
</div>
<input type="checkbox" id="title2" name="contentbox" />
<label for="title2">Content 2</label>
<div class="content">
<div class="tooltip"><div class="whitepaper" onclick="myFunction()"><div class="blackframe"><span class="tooltiptext">Copy link 2 to clipboard</span>URL</div></div><input type="text" value="https://mywebsite.com/#title2" id="myInput"></div>
</div>
<input type="checkbox" id="title3" name="contentbox" />
<label for="title3">Content 3</label>
<div class="content">
<div class="tooltip"><div class="whitepaper" onclick="myFunction()"><div class="blackframe"><span class="tooltiptext">Copy link 3 to clipboard</span>URL</div></div><input type="text" value="https://mywebsite.com/#title3" id="myInput"></div>
</div>
<script>
function myFunction() {
/* Get the text field */
var copyText = document.getElementById("myInput");
/* Select the text field */
copyText.select();
copyText.setSelectionRange(0, 99999); /* For mobile devices */
/* Copy the text inside the text field */
navigator.clipboard.writeText(copyText.value);
/* Alert the copied text */
alert("Copied: " + copyText.value);
}
</script>
</body>
</html>
```
Replace function myFunction like this:
function myFunction(event) {
var target = event.target;
var copyText = target.nextElementSibling;
navigator.clipboard.writeText(copyText.value);
alert("Copied: " + copyText.value);
}
then update all onclick attributes like this
onclick="myFunction(event)"
I found a few issues with your code
You didn't change the id number on the inputs so they all would alert to the same URL which made it difficult to tell which is being clicked on.
You are doing a query selection on an id that appears multiple times. This means it is not being fired on the clicked element.
My approach includes taking advantage of the clicked element by passing it in your click handler.
<div class="tooltip">
<div class="whitepaper" onclick="myFunction(event)">
<div class="blackframe"><span class="tooltiptext">Copy link 3 to clipboard</span>URL</div>
</div><input type="text" value="https://mywebsite.com/#title3" id="myInput">
</div>
This lets me pass that event to the function call which will give us access to the current target node.
function myFunction(event) {
/* Get the text field */
var copyText = event.target.parentNode.nextSibling.nextSibling.value
/* Copy the text inside the text field */
navigator.clipboard.writeText(copyText);
/* Alert the copied text */
alert("Copied: " + copyText);
}
In the above case, I had to do some weird traversing because your input is outside the scope of the clicked element. I removed the code related to mobile stuff because that wasn't relevant to this issue (feel free to put that back in).
here's the codepen with my example.
I am trying to float the labels onfocusing input in angular.
I did wrote code to perform the action of a class to be applied on focusing the input, & then focus out to check empty value or values present, to remove the applied class.
forms.component.html
<div class="forms-field-box">
<div class="forms-field-inp-box">
<input id="forms_field_inp_fn" class="forms-field-inp" type="text" (focus)=inpAnim();>
<label for="forms_field_inp_fn" id="forms_input" class="forms-field-inp-lbl">
FirstName
</label>
</div>
</div>
<div class="forms-field-box">
<div class="forms-field-inp-box">
<input id="forms_field_inp_ln" class="forms-field-inp" type="text" (focus)=inpAnim();>
<label for="forms_field_inp_ln" id="forms_input" class="forms-field-inp-lbl">
LastName
</label>
</div>
</div>
<div class="forms-field-box">
<div class="forms-field-inp-box">
<input id="forms_field_inp_p" class="forms-field-inp" type="text" (focus)=inpAnim();>
<label for="forms_field_inp_p" id="forms_input" class="forms-field-inp-lbl">
Password
</label>
</div>
</div>
<div class="forms-login-btn">
Login
</div>
forms.component.scss
margin: 0px;
padding: 0px;
box-sizing: border-box;
}
.forms-container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 60%;
background-color: #ffffff;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.5);
min-height: 120px;
padding: 20px;
border-radius: 5px;
}
.forms-field-box {
background-color: #ffffff;
margin-bottom: 15px;
}
.forms-field-inp-box {
width: 97%;
position: relative;
}
.forms-field-inp {
width: 100%;
height: 35px;
border-radius: 5px;
border: 1px solid #e4e4e4;
outline: none;
padding: 6px;
}
.forms-field-inp:hover {
border: 1px solid #cccccc;
}
.forms-field-inp:focus {
border: 1px solid #0089ff;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
.forms-field-inp-lbl {
position: absolute;
top: 8px;
left: 10px;
font-size: 14px;
font-weight: 600;
background-color: #ffffff;
padding: 2px 6px;
user-select:none;
color: #cccccc;
}
.forms-field-inp-lbl-anim ~ .forms-field-inp-lbl {
top: -9px;
}
.forms-login-btn {
width: 20%;
height: 35px;
text-align: center;
font-size: 18px;
padding: 5px;
background-color: blue;
border: 1px solid blue;
border-radius: 5px;
margin: 10px auto;
cursor: pointer;
box-shadow: rgba(99, 99, 99, 0.2) 0px 2px 8px 0px;
}
forms.component.ts
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-forms',
templateUrl: './forms.component.html',
styleUrls: ['./forms.component.scss']
})
export class FormsComponent implements OnInit {
constructor() {
}
ngOnInit(): void {
}
inpAnim(){
let forms_input_ele:any = document.querySelector(".forms-field-inp-box input");
forms_input_ele.addEventListener("focusin", () => {
console.log("focussed in");
forms_input_ele.classList.add("forms-field-inp-lbl-anim");
} );
forms_input_ele.addEventListener("focusout", () => {
let forms_inp_val = forms_input_ele.value;
if(forms_inp_val == ""){
forms_input_ele.classList.remove("forms-field-inp-lbl-anim");
console.log("focussed out");
console.log(forms_inp_val);
}
});
}
}
Please Help me to solve this.
input focused & label floated
input has value & label stays
Problem: But, the first input only works, other inputs not working if focused & nothing happens.,
Next input is focused, but label not floating
This can be easily applied by css, but need to make this small code to work correctly
I currently have a functionality where the user can upload and display multiple images at once as a preview on top of a modal. Ideally, I would like to allow the user to click on one of the displayed images and add it to an array on a click event so that they can then hit a button to submit the images for processing somewhere. I've tried for a few hours trying to code this out but have hit a brick wall and wouldn't mind some guidance on the matter! I'd love to be able to implement only HTML, CSS and vanilla JS... any suggestions or offerings of help would be appreciated! I searched online a fair bit but couldn't really grasp many of the concepts offered...
<!--Modal code: -->
<div id="simpleModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<span class="closeBtn">×</span>
<h2>Image search and processing: </h2>
</div>
<div class="modal-body">
<form id="modal-form" class="form">
<label for="files">Select multiple files: </label>
<input id="files" target="_blank" type="file" onchange="previewFiles()" multiple/>
<output id="result">
<button type="submit" class="floating-btn" value ="submit">+</button>
<button type="reset" class="floating-btn2" value ="reset" onclick="return hideImage()">x</button>
</form>
</div>
<div id="preview"></div>
</div>
</div>
<script>
//code to render image files to modal
function previewFiles() {
var preview = document.querySelector('#preview');
var files = document.querySelector('input[type=file]').files;
function readAndPreview(file) {
// Make sure `file.name` matches our extensions criteria
if ( /\.(jpe?g|png|gif)$/i.test(file.name) ) {
var reader = new FileReader();
reader.addEventListener("load", function () {
var image = new Image();
//styling in JS //
image.height = 160;
image.width = 160;
image.style.flexDirection = "row";
image.title = file.name;
image.src = this.result;
preview.appendChild( image );
}, false);
reader.readAsDataURL(file);
}
}
if (files) {
[].forEach.call(files, readAndPreview);
}
}
//delete form
function hideImage() {
document.getElementById("modal-form").reset(); //reset form
var preview = document.querySelector("#preview");
preview.innerHTML = '' //set preview to null
</script>
}
/* floating buttons: */
.floating-btn{
width: 80px;
height: 80px;
background: #0B406D;
display: flex;
border-radius: 50%;
color: white;
font-size: 40px;
align-items: center;
justify-content: center;
text-decoration: none;
box-shadow: 2px 2px 5px rgba(0,0,0,0.25);
position: fixed;
right: 120px;
bottom: 20px;
outline: blue;
border: none;
cursor: pointer;
}
.floating-btn:hover {
background: #4D89C8;
}
.floating-btn2{
width: 80px;
height: 80px;
background: #0B406D;
display: flex;
border-radius: 50%;
color: white;
font-size: 40px;
align-items: center;
justify-content: center;
text-decoration: none;
box-shadow: 2px 2px 5px rgba(0,0,0,0.25);
position: fixed;
right: 20px;
bottom: 20px;
outline: blue;
border: none;
cursor: pointer;
}
.floating-btn2:hover {
background: #4D89C8;
}
/*Modal styling: */
.modal{
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
position: fixed;
top: 0;
z-index: 1;
display: none;
justify-content: center;
align-items: center;
}
.modal-content{
width: 80%;
height: 80%;
background-color: rgba(255, 255, 255, 0.9);
border-radius: 4px;
padding: 15px;
margin: 20% auto;
box-shadow: 0 5px 8px 0 rgba(0, 0, 0, 0.2), 0 7px 20px rgba(0, 0, 0, 0.17);
animation-name: modalopen;
animation-duration: 1s;
flex-direction: column;
justify-content: space-around;
}
.modal-header{
font-size:12pt;
color: black;
}
.modal-header h2{
margin: 0;
}
.modal-body{
width: 33.33%;
padding: 5px;
}
.closeBtn{
color: #ccc;
float: right;
font-size: 50px;
}
.closeBtn:hover,.closeBtn:focus{
color: red;
text-decoration: none;
cursor: pointer;
}
#keyframes modalopen{
from{opacity: 0}
to {opacity: 1}
}
/*Image displaying style: */
form{
margin-top:10px;
padding: 5px;
border-radius: 4px;
margin: 0 auto;
}
a img{
float: left;
width: 150px;
height: 150px;
padding-right: 15px;
box-sizing: border-box;
}
img:hover {
transform: scale(1.5);
cursor: pointer;
}
While there can be complicated ways like using FormData and ajax to submit the form it's better to always go with simpler ones. One of such is below
Add a hidden field in your form
<input type="hidden" name="list" id="list">
</form>
Create a variable that will hold an array var sList = []
Now just take this function it appends to that array and serializes that data into the hidden field
function addToArr(item){
var index = sList.indexOf(item);
if (index == -1) { //if not already added
sList.push(item) // add
}
document.getElementById('list').value = JSON.stringify(sList);
}
That's it now in your reader load event listener add this one line
reader.addEventListener("load", function () {
var image = new Image();
//other code
//..
image.src = this.result;
image.onclick = function(){ addToArr(file.name); } // <-- this line
preview.appendChild( image );
}, false);
Now whenever user clicks any image, its name will get into the list array and therefore in hidden field (You may verify using inspect element in devtools). So when you submit the form, on you server side just decode/parse the json and you know which images user wants (the names) by that you can filter on the actual file objects. You haven't told what language you are using at the server so for now this should suffice
I have a pen, which is basically a todo app. The todo items are actually li elements which have text, button and a hr. Some of them are having hr with spaces inside them while some doesn't.
Image:
HTML:
const j = $;
j(() => {
let validify = txt => {
if (txt.length > 0) {
j('#ctn').append(`<li class='td'>${txt}<button class='td-btn'>Dismiss</button><hr/></li>`);
}
j('.td-btn').on('mouseenter', function() {
console.log('added');
j(this)
.parent()
.addClass('del');
console.log(j(this).parent().attr('class'))
}).on('mouseleave', function() {
console.log('removed')
j(this)
.parent()
.removeClass('del');
}).on('click', function() {
j(this).parent().css('display', 'none');
});
j('#addtd').val('');
}
validify('');
j('#btn').on('click', () => {
validify(j('#addtd').val());
});
});
#import url("https://fonts.googleapis.com/css?family=Lato");
* {
box-sizing: border-box;
font-family: Lato;
}
body {
margin: 0;
padding: 3vh 7vw;
background: #004D40;
}
#in-ctn {
position: fixed;
width: 86vw;
height: 16vh;
background: #388E3C;
box-shadow: 0 6px 9px #272727;
z-index: 2;
}
#btn {
position: absolute;
border-radius: 100%;
outline: none;
border: none;
right: 7vh;
top: 3vh;
width: 10vh;
height: 10vh;
font: 500 8vh arial;
display: inline-block;
transition: 0.25s all;
background: #CDDC39;
}
#btn:hover {
box-shadow: 0 2px 1px rgba(0, 0, 0, 0.33);
transform: scale(1.1);
}
#btn:active {
transform: translateY(4px);
}
#addtd {
position: absolute;
outline: none;
border: none;
background: rgba(255, 255, 255, 0.33);
width: 50vw;
height: 6vh;
top: 5vh;
left: 5vw;
font: 500 14pt Lato;
padding: 0 10px;
}
#addtd::placeholder {
color: #FFF;
}
#ctn {
position: absolute;
top: 27vh;
width: 86vw;
background: #388E3C;
box-shadow: 0 6px 9px #272727;
padding: 3vh 5vw;
z-index: 1;
}
li.td {
font: 500 20pt Lato;
list-style: none;
color: #FFF;
}
button.td-btn {
float: right;
outline: none;
border: none;
background: #E53935;
height: 20px;
position: relative;
top: 25px;
color: #FFF;
}
hr {
border: 7px solid #9E9D24;
padding: 0;
}
.del {
color: #CDDC39 !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='main'>
<div id='in-ctn'>
<button id='btn'>+</button>
<input type='text' id='addtd' placeholder='Enter a new Todo' />
</div>
<div id='ctn'>
<li class='td'>
Code a Todo App
<button class='td-btn'>Dismiss</button>
<hr/>
</li>
<li class='td'>
Style the Elements
<button class='td-btn'>Dismiss</button>
<hr/>
</li>
<li class='td'>
Debug some problems
<button class='td-btn'>Dismiss</button>
<hr/>
</li>
<li class='td'>
Go for a walk
<button class='td-btn'>Dismiss</button>
<hr/>
</li>
</div>
</div>
Can anyone explain me why it is so?
This is happening due to CSS Sub pixel rendering.
When you zoom-in/out of the browser, the rescaled elements will have left over pixel values like 5.75px etc. The vendor decides how to deal with that.
In your case the easiest fix, at least in Chrome, is to cancel the border radius to 0px, instead set the height of the hr to double the border and give it a background color:
border: 0px solid #9E9D24;
padding: 0;
height: 14px;
background: #9E9D24;
Seems like this issue is browser related, since it works fine for most people. Possibly your browser has a default styling for hr elements. It is, however, nowadays bad practice to use a horizontal line for presentational terms. Source
You would be fine by using a border-bottom on your li element. If you want to position the border lower than the default position, you can use padding-bottom on the li element. Your HTML structure also looks a lot more clear with this.
For example, changing the styling of your CSS selector li.td to the following could do the trick:
li.td {
font: 500 20pt Lato;
list-style: none;
color: #CDDC39;
border-bottom: 10px solid #9E9D24;
padding-bottom: 10px;
margin-bottom: 10px;
}
In case you really need to use the hr element, you could attempt to remove all default margin since some browsers add a margin by default. For that, add the following styling to the element:
margin: 0
which would result into
hr {
border: 7px solid #9E9D24;
padding: 0;
margin: 0;
}
Did you edit your pen to fix the issue? When looking at your pen preview all <hr> tags are rendered without an empty space inside.
The only suggestion I have, is that in HTML <hr> doesn't need to be explicitly closed, unless you are using XHTML, then you need to properly close the tag <hr />. Since you are just writing HTML, I would go with the <hr>.
I made a form control which uses as its container (see the Yes/No toggler below)
Here's the code for that:
<span class="toggle">
<i>Yes</i>
<i>No</i>
<input type="hidden" name="toggle-value" value="0">
</span>
My CSS isn't relevant to the question, but it's included for comprehension of my control:
.toggle { width:auto; height: 20px; display: inline-block; position: relative; cursor: pointer; vertical-align: middle; padding: 0; margin-right: 27px; color: white !important;}
.toggle i { display: block; padding: 0 12px; width: 100%; height: 100%; -webkit-border-radius: 12px; -moz-border-radius: 12px; border-radius: 12px; text-align: center; font: 11px/20px Arial !important; text-transform: uppercase; }
.toggle i:first-child { -moz-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5) inset; box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5) inset; background-color: #73B9FF; }
.toggle i:last-child { -moz-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5) inset; box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5) inset; background-color: #cc0000; position: relative; top: -20px; z-index: -1; }
.toggle.on i:first-child { z-index: 1; } /* they overlap but on click they switch who gets to be on top. */
.toggle.on i:last-child { z-index: -1; }
.toggle.off i:first-child { z-index: -1; }
.toggle.off i:last-child { z-index: 1; }
.toggle.off i:last-child:before { content: " "; display:block; position:absolute; left:1px; top:1px; text-indent: -9999px; width: 18px; height: 18px; -webkit-border-radius: 11px; -moz-border-radius: 11px; border-radius: 11px; z-index: 1; background-color: #fff; } /* circle */
.toggle.on i:first-child:after { content: " "; display:block; position:absolute; right:-23px; top:1px; text-indent: -9999px; width: 18px; height: 18px; -webkit-border-radius: 11px; -moz-border-radius: 11px; border-radius: 11px; z-index: 1; background-color: #fff; } /* circle */
and the JS that makes it all work:
.on('click', '.toggle', function(){
var input = $(this).next('input');
if(input.val() == 1) {
$(this).removeClass('on').addClass('off');
input.val(0).change();
} else {
$(this).removeClass('off').addClass('on');
input.val(1).change();
}
}
The problem is that I'm using this all over my application for data-entry. Have you ever wanted to NOT use the mouse when you're entering a lot of data? Yeah, me too. So you hit TAB and a toggle like this should respond to the spacebar. But instead, since it's just a element, it is skipped altogether.
I'm hoping someone can help me solve the question of "how the heck do I make this a tab stop AND be in the correct order"?
==============
EDIT: HERE IS MY UPDATED JQUERY CODE CONTAINING THE SOLUTION:
$('.toggle').click(function(){
var input = $(this).next('input');
if(input.val() == 1) {
$(this).removeClass('on').addClass('off');
input.val(0).change();
} else {
$(this).removeClass('off').addClass('on');
input.val(1).change();
}
}).focus(function() {
$(this).bind('keydown', 'space', function(e){
$(this).trigger('click')
e.preventDefault();
});
}).blur(function() {
$(this).unbind('keydown');
}).attr('tabIndex', 0);
Try setting your tabindex to 0, on the non-anchor elements you would like to make "tabbable". For example tabindex="0". This way, you won't mess with the tabbing order, or have to throw tabindexs all over the place.
Look into the html attribute tabindex. Basically you should be able to set tabindex on each input you want to focusable via the tab key. Start the first one a 1 and just count upwards for each input. If you also want to take an input out of focusing via the tab key set the tabindex to -1.