positioning a button child wihtin a list parent - javascript

I am working on a to-do list. Whenever the user enters a new task, it is displayed as a list item. Within the list item I am attempting to position a button element, so that when the user clicks on it, the item can shift into another list - "completed tasks".
I am having a bit of trouble poistioning the button element within the list to suit my requirements. I am trying to posistion it equivalant to "right-aligned", at the end of the list element, so that the button is after the text has finished of the given to-do task (I hope that makes sense!)
HTML:
<div id="incomplete-tasks">
<h4>INCOMPLETE TASKS</h4>
<ul id="task-to-do">
</ul>
</div>
CSS:
ul {
list-style: none;
}
ul li {
position: relative;
margin: auto;
width: 80%;
padding: 10px;
margin-bottom: 10px;
color: white;
border: 1px solid white;
border-radius: 3px;
background-color: #6363B6;
}
li button {
display: block;
width: auto;
padding: 1%;
position: absolute;
margin-left: 80%;
}
JS:
document.getElementById("add").addEventListener("click", function () {
var taskinput = document.getElementById("task").value;
if (taskinput) {
var tasktext = document.createTextNode(taskinput);
var list = document.createElement("li");
list.appendChild(tasktext);
var button = document.createElement("button");
button.innerHTML = "completed";
list.appendChild(button);
document.getElementById("task-to-do").appendChild(list);
document.getElementById("task").value ="";
} else {
alert("Please enter a task");
}
});

Try using this, It may help you:
li button {
display: block;
width: auto;
padding: 1%;
clear:both;
float:right;
}
Try this float property It may help you.

Related

How do I use drag and drop with dynamically created HTML? (SortableJS)

I'm starting to learn javascript and I have a simple todo application where I want to be able to drag and drop the different todo's created. A simple way to do this was with SortableJS library, but it doesn't work the way i want to. After implementing the simple sortable function, when dragging on a todo, it grabs the whole todo-list instead of a single todo
I think the issue is because I dynamically create the html, but I'm kinda stuck and would appreciate any suggestions.
//Selectors
const todoInput = document.querySelector(".todos-input"); //input for adding a todo
const todoButton = document.querySelector(".todos-button"); //add todo-button
const todoList = document.querySelector(".todos-list"); //the todo-list
//Event listeners
todoButton.addEventListener("click", addTodo);
todoList.addEventListener("click", deleteTodo);
todoList.addEventListener("click", completeTodo);
//Functions
function addTodo(event) {
//prevent form from submitting
event.preventDefault();
//create a div for the todos-list
const todoDiv = document.createElement("div");
//add classlist for styling
todoDiv.classList.add("todo");
//Create LI
const newTodo = document.createElement("li");
//output the value from the add-todo field
if (todoInput.value != "") {
newTodo.innerText = todoInput.value;
} else {
return false;
}
//classlist for styling
newTodo.classList.add("todo-item");
//append child to div
todoDiv.appendChild(newTodo);
//complete button
const completedButton = document.createElement("button");
completedButton.innerHTML = '<i class="fas fa-check"><i/>';
completedButton.classList.add("completed-btn");
todoDiv.appendChild(completedButton);
//delete button
const deletedButton = document.createElement("button");
deletedButton.innerHTML = '<i class="fas fa-trash"><i/>';
deletedButton.classList.add("deleted-btn");
todoDiv.appendChild(deletedButton);
//drag button
const dragButton = document.createElement("button");
dragButton.innerHTML = '<i class="icon fa fa-bars"><i/>';
dragButton.classList.add("drag-btn");
dragButton.classList.add("handle");
todoDiv.appendChild(dragButton);
//append div to list of todos
todoList.appendChild(todoDiv);
//clear input field after adding a new todo
todoInput.value = "";
//DRAG AND DROP
const dragArea = document.querySelector('.todos-section');
new Sortable(dragArea, {
animation: 300
});
}
//deleting todo
function deleteTodo(e) {
//grab the item, whatever we are clicking on
const item = e.target;
//delete todo
if (item.classList[0] === "deleted-btn") {
//grab the parent element of the item, which is the todolist element in this case
const todo = item.parentElement;
//remove the todo
todo.remove();
}
}
//completing todo
function completeTodo(e) {
//grab the item, whatever we are clicking on
const item = e.target;
//complete todo
if (item.classList[0] === "completed-btn") {
const todo = item.parentElement;
//use the toggle because if the element has a class, then the classList.toggle method
//behaves like classList.remove and the class is removed from the element.
//And if the element does not have the specified class
//then classList.toggle, just like classList.add, adds this class to the element.
//So it basically does the add/remove operation for us depending on the state.
todo.classList.toggle("completed-todo");
}
}
/*Apply to all elements*/
* {
box-sizing: border-box;
list-style-type: none;
margin: 0;
padding: 0;
}
body {
font-family: "Merriweather Sans", sans-serif;
background: rgba(216, 206, 206, 0.787);
}
.wrapper {
display: flex;
position: relative;
}
/* Add todos-section */
.todos-bar {
position: fixed;
top: 5%;
left: 50%;
font-size: 17px;
border: 0;
transform: translate(-50%, -50%);
padding-left: 100px;
}
.todos-bar input {
width: 600px;
height: 50px;
border: 0px;
outline: none;
font-size: 20px;
padding-left: 20px;
border-radius: 5px;
}
.todos-bar button {
position: fixed;
background: rgba(20, 33, 93, 0.952);
color: white;
font-size: 20px;
border: 0;
outline: none;
height: 50px;
padding: 10px 20px;
right: 0px;
border-radius: 0px 5px 5px 0px;
cursor: pointer;
}
.todos-bar button:hover {
background: rgb(43, 54, 73);
}
/* Todos section */
.todos-section {
display: flex;
position: fixed;
top: 15%;
left: 37%;
}
.todos-list {
width: 600px;
}
.todo {
margin: 1.5rem;
background: white;
color: black;
font-size: 1.5rem;
display: flex;
justify-content: space-between;
align-items: center;
border-radius: 5px;
padding-left: 0.5rem;
margin: 15px;
transition: all 0.5s ease;
}
.todo li {
flex: 1;
}
.todo-item {
padding: 0rem 0.5rem;
padding-left: 2.5rem;
}
.deleted-btn,
.completed-btn {
background: rgb(248, 56, 56);
color: white;
border: none;
padding: 1rem;
cursor: pointer;
font-size: 1rem;
}
.completed-btn {
background: green;
}
.deleted-btn {
border-radius: 0px 5px 5px 0px;
}
.drag-btn {
display: block;
position: absolute;
background: white;
border: 2px solid white;
}
.fa-bars {
padding: 5px;
margin: 2px;
cursor: pointer;
}
.fa-trash,
.fa-check {
pointer-events: none;
}
.completed-todo {
text-decoration: line-through;
opacity: 0.5;
}
<head>
<link rel="stylesheet" href="style.css">
<script src="https://kit.fontawesome.com/47440aba67.js" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.14.0/Sortable.min.js"></script>
</head>
<body>
<div class="wrapper">
<!--ADD TODO-->
<div class="todos-bar">
<input type="text" class="todos-input" placeholder="Add to list...">
<button class="todos-button" type="submit"><i class="fas fa-plus"></i></button>
</div>
<!--TODO LIST-->
<div class="todos-section">
<ul class="todos-list"></ul>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
According to the documentation,
You can use any element for the list and its elements, not just ul/li
What you've implemented actually fits in this description since there is a ul with div tags inside. However, you are not referencing the right element in the dragArea, because it should be the direct parent (.todos-list) of your desired draggable children.
So, change it to .todos-list and in addition pass handle property to Sortable constructor to reference the icon in which you want to drag.
const dragArea = document.querySelector('.todos-list');
new Sortable(dragArea, {
animation: 300,
handle: '.fa-bars'
})
Working example

Issue with ToDO list app and list item length

I'm working on a Q/A bare bones todolist app and notice that when a list item that is really long is added to the list, it pushes the button out.
Is there a way I can make the LI element larger when the textnode hits the button margin instead of pushing the button out of the LI element. Below is a screenshot. I'll post my source code below, but maybe this is a question that is a quick fix?
My source code can be found here - Issue with floating buttons right of my to do list
A) If I understood you well, you can easily fix it with CSS-Grid:
li {
display: grid;
grid-template-columns: 3fr 100px;
grid-template-areas: 'text button';
}
li > span {
grid-area: text;
}
li > button {
grid-area: button;
height: 30px;
}
https://jsfiddle.net/axqwhj29/
Play with the example linked above resizing the result area to check if that's what you are looking for.
B) Also, but I don't recommend you, if you really don't wanna change your li hight and you have a maximum text width (ex: 25 characters), you can clip parts of your message in a phone vertical view and if the user flips to horizontal show the whole text automatically.
https://jsfiddle.net/qfy3mz01/
Hope this help :)
Okay I have wrapped the text inside the li with span element and and added I add grid display to li and give every element inside the li a width and then I have added word-break: break-word; so the line will break when the text of the span reach the width limit and don't affect the delete button and I've deleted height from li so the li will grow with the lines on it
var addItemButton = document.getElementById('addItem')
var onEnter = document.getElementById('newNote')
//below event listener adds an item to the list on click
addItemButton.addEventListener('click', function() {
let item = document.getElementById('newNote').value
let node = document.createElement("li")
let span = document.createElement("span")
let textnode = document.createTextNode(item)
span.appendChild(textnode)
node.appendChild(span)
if (item) {
document.getElementById('list-body').appendChild(node)
}
let node2 = document.createElement('BUTTON')
let textnode2 = document.createTextNode('Delete')
node2.appendChild(textnode2)
node.appendChild(node2)
node2.addEventListener('click', function() {
node2.parentNode.parentNode.removeChild(node)
});
document.getElementById('newNote').value = ''
});
onEnter.addEventListener('keyup', function(event) {
if (event.keyCode === 13) {
// Cancel the default action, if needed
event.preventDefault();
// Trigger the button element with a click
addItemButton.click();
}
})
function applyButton() { //onload for dummy data or data from db
let getListObjects = document.querySelectorAll("li")
for (let i = 0; i < getListObjects.length; i++) {
let node2 = document.createElement('BUTTON')
let textnode2 = document.createTextNode('Delete')
node2.appendChild(textnode2)
getListObjects[i].appendChild(node2)
let y = getListObjects[i].querySelector('button')
y.addEventListener('click', function() {
y.parentNode.parentNode.removeChild(getListObjects[i])
});
}
}
.container {
height: 100%;
width: 40%;
margin: 0 auto;
}
.container2 {
display: grid;
grid-template-columns: repeat(2, 1fr);
background-color: grey;
border: 1px solid grey;
}
#main-grid {
width: 100%;
}
#newNote {
height: 25px;
}
#inputIdForGrid {
justify-content: left;
align-items: center;
display: flex;
padding-left: 0.3em;
padding-top: 0.5em;
padding-bottom: 0.5em;
}
button {
padding: 10px 18px;
background-color: green;
border: none;
color: white;
font-size: 14px;
align-self: center;
justify-self: end;
}
#addItem {
margin-left: 1em;
padding: 0.5em;
color: white;
font-size: 1.5em;
float: right;
}
ul {
list-style-type: none;
padding: 0px;
margin: 0px;
}
li {
padding: 5px 15px;
display: grid;
grid-template-columns: 2.5fr .5fr;
}
span {
word-break: break-word;
grid-column: 1 / 2;
display: flex;
align-items: center;
}
li:nth-child(2n) {
background-color: grey;
}
li>button {
background-color: red;
}
h1 {
text-align: center
}
<body onload="applyButton()">
<h1>Vanilla JS ToDo List - No Jquery, No Bootstrap</h1>
<div class='container'>
<div id='main-grid'>
<div class="container2">
<div id='inputIdForGrid'>
<input type='text' placeholder="Enter List Items Here" id='newNote'>
</div>
<div>
Hi
</div>
</div>
<ul id='list-body'>
<li><span>run all around town. walk all around town. drive all around town</span></li>
<li><span>Buy Apples</span></li>
<li><span>Hit Gym and Lift Bro</span></li>
<li><span>Stretch</span></li>
</ul>
</div>
</div>
</body>
P.S. I've edited your js code so it will generate span and add the text inside it

Clone and Change on new radio check

Firstly I don't want to edit the html but use JavaScript/jQuery to achieve this.
One input is selected(checked) by default and in this example 'Express Shipping'. Then all of the html is cloned / copied from within the parent which is < li > that is holding the input. I don't need the < li > copied just the input within it.
This is then placed inside the #dumpinfohere < div >.
I can manage up to this point, however I want it so when I then toggle between both radio buttons, or if I add additional radio buttons then the complete html of that checked input plus it's < li > replaced the #dumpinfohere section.
This is the code I've done to try and achieve this:
JSFiddle. You can view what I've done here.
HTML
<ul id="shipping_method">
<li>
<input type="radio" name="shipping_method[0]" data-index="0" id="shipping_method_0_betrs_shipping_12-1" value="betrs_shipping_12-1" class="shipping_method "> Free Shipping
</li>
<li>
<input type="radio" name="shipping_method[0]" data-index="0" id="shipping_method_0_betrs_shipping_12-2" value="betrs_shipping_12-2" class="shipping_method" checked="checked"> Express Shipping
</li>
</ul>
Javascript
jQuery(document).ready(function($){
var changeShip = function() {
$('#shipping_method input:checked')
.parent()
.clone()
.appendTo(".woocommerce-billing-fields #dumpinfohere");
};
$('#shipping_method input').change(changeShip);
changeShip();
});
CSS
#shipping_method {
float: left; width: 100%;
list-style: none;
padding: 0px;
}
#shipping_method li {
/* display: none; */
float: left; width: 100%;
background: #ccc;
padding: 5px 10px;
}
#shipping_method .red { background: red; }
.woocommerce-billing-fields {
background: #000; color: #fff;
width: 100%; height: 300px;
padding: 5px 10px;
float: left;
}
.woocommerce-billing-fields li { padding: 10px 0px; color: #e024a7; list-style: none; }
.woocommerce-billing-fields li input { display: none; }
However you can see if you toggle between the two radio buttons it doesn't change/replace the text but rather just adds to it?
You just need to add
$("#dumpinfohere").html('');
after
var changeShip = function() {
So your final code would be:
jQuery(document).ready(function($){
var changeShip = function() {
$("#dumpinfohere").html('');
$('#shipping_method input:checked')
.parent()
.clone()
.appendTo(".woocommerce-billing-fields #dumpinfohere");
};
$('#shipping_method input').change(changeShip);
changeShip();
});
Basically, you need to empty the container before appending any new text in it.

Translating jQuery to Vanilla JS issue

I was watching a tutorial that used jQuery and wanted to turn it into JS, but my code is broken - was hoping someone could help me with this:
Tutorial JS:
$(function() {
var btn = $('button');
var progressBar = $('.progressbar');
btn.click(function() {
progressBar.find('li.active').next().addClass('active');
})
})
Taken from URL:http://www.kodhus.com/kodity/codify/kod/mGXAtb
Here is my failed attempt at rewriting the jQuery using JavaScript DOM:
var btn1 = document.getElementsByTagName('BUTTON');
var progBar = document.getElementsByClassName('progressbar');
function clickMe1() {
var elm = progBar.querySelectorAll("li");
var emlClass = elm.querySelector(".active");
return emlClass.nextElementSibling.addClass('active');
}
btn1.addEventListener("click", clickMe1, false);
where did I go wrong?
Working fiddle.
Your code will work after several changes check the notes below :
You've missed addClass() there it's a jQuery function, for vanilla JS use .classList.add() instead:
return emlClass.nextElementSibling.classList.add("active");
querySelectorAll(); will return a list of nodes you have to loop through them and add class, use :
var emlClass = progBar.querySelectorAll("li.active");
Instead of :
var elm = progBar.querySelectorAll("li");
var emlClass = elm.querySelector(".active");
Then loop and add active class:
for(var i=0;i<emlClass.length;i++){
emlClass[i].nextElementSibling.classList.add("active");
}
getElementsByTagName() and getElementsByClassName() will also returns a list of nodes with given name, you have to specify which one you want to pick (selecting the first in my example) :
var btn1 = document.getElementsByTagName('BUTTON')[0];
var progBar = document.getElementsByClassName('progressbar')[0];
Hope this helps.
var btn1 = document.getElementsByTagName('BUTTON')[0];
var progBar = document.getElementsByClassName('progressbar')[0];
function clickMe1() {
var emlClass = progBar.querySelectorAll("li.active");
for(var i=0;i<emlClass.length;i++){
emlClass[i].nextElementSibling.classList.add("active");
}
}
btn1.addEventListener("click", clickMe1, false);
.container {
width: 100%;
}
.progressbar {
counter-reset: step;
margin: 0;
margin-top: 50px;
padding: 0;
}
.progressbar li {
list-style-type: none;
float: left;
width: 33.33%;
position: relative;
text-align: center;
}
.progressbar li:before {
content: counter(step);
counter-increment: step;
width: 30px;
height: 30px;
line-height: 30px;
border: 2px solid #ddd;
display: block;
text-align: center;
margin: 0 auto 10px auto;
border-radius: 50%;
background-color: white;
}
.progressbar li:after {
content: '';
position: absolute;
width: 100%;
height: 2px;
background-color: #ddd;
top: 15px;
left: -50%;
z-index: -1;
}
.progressbar li:first-child:after {
content: none;
}
.progressbar li.active {
color: green;
}
.progressbar li.active:before {
border-color: green;
}
.progressbar li.active + li:after {
background-color: green;
}
button {
position: relative;
border: none;
padding: 10px 20px;
font-size: 16px;
border-radius: 2px;
left: 50%;
margin-top: 30px;
transform: translate(-50%);
cursor: pointer;
outline: none;
}
button:hover {
opacity: 0.8;
}
<div class="container">
<ul class="progressbar">
<li class="active">Step 1</li>
<li>Step 2</li>
<li>Step 3</li>
</ul>
</div>
<button>Next step</button>
.querySelectorAll("li") will return an array (or an array-like object) with one or more <li> tags. So you need to either:
loop through every <li> in that list and do the rest,
or just take the first item from that list if you don't want to worry about there being more than one li in the page,
or use .querySelector (not .querySelectorAll) to just take the first <li> for you.
MDN

How can I correct this to make it look better?

I'm trying to create a list of items that can be accessed when the menu is clicked on but im having trouble with the CSS. Ive managed to remove the bullet points but it leaves a massive gap between the border and the edge of the div. I would like to know if theres an easy way of centralising the table in the div, something that works like text-align or similar. I would prefer something that requires an automatic touch rather than just guess the width of pixels.
This is the CSS I've created so far :
.submenudiv{
display:inline-block;
/*height:170px;
width:120px;*/
background-color: grey;
/*border: 2px black solid;*/
margin: auto;
/*margin-top: 50px;*/
padding: 5px;
text-align: center;
}
.submenudiv: hover{
background-color:blue;
}
.list{
list-style-type: none;
/*margin-left: -40px;
margin-top:3px;*/
border: 2px solid black;
padding: 5px;
text-align: left;
}
.list:hover{
background-color:blue;
color:white;
}
The JS code to this is :
var list = function () {
var creatDiv = document.createElement("div");
creatDiv.id = "submenudiv";
creatDiv.className = "submenudiv";
var creatul = document.createElement("ul");
for(index = 0; index < 5; ++index){
li = document.createElement("li");
li.className = "list";
li.innerHTML = "Submenu" + index;
creatul.appendChild(li);
}
creatDiv.appendChild(creatul);
document.body.appendChild(creatDiv);
};
creatbtndiv.onclick = function () {
var alert = confirm("yes master");
list();
};
Just click on "Click Me", confirm the command to proceed and a table should show up.
Appreciate the help.
remove gap
ul {
margin:0;
padding:0;
}
center table
.divContainer{
text-align:center;
}
table {
margin:0 auto;
}

Categories