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
Related
I'm making a website for myself and it has a projects page and I made a js script to auto add each of the projects using a json file. I have 4 projects in the json file so far but one of them is acting up.
Circled in red is the div that is messed up.
Here is the css for those divs:
#img {
vertical-align: top;
display: inline-block;
border-radius: 10px 10px 0px 0px;
width: 340px;
}
#title {
margin-top: 40px;
vertical-align: top;
font-weight: bold;
font-size: 30px;
float: left;
margin-left: 5px;
display: inline-block;
}
#desc {
float: left;
margin-left: 5px;
display: inline-block;
}
Now here is the js script that is adding them:
$.getJSON('https://raw.githubusercontent.com/Pinball3D/andysthings/main/registy.json',
function(data){
console.log(data);
var i = 0;
console.log(data.length, i < data.length)
while (i < data.length) {
dat = data[i];
var a = document.createElement("a");
a.href=dat.url;
var li = document.createElement("li");
li.id="item";
var img = document.createElement("img");
img.src=dat.img;
img.id="img";
li.appendChild(img);
var title = document.createElement("div");
title.id="title";
title.textContent=dat.name;
li.appendChild(title);
var desc = document.createElement("div");
desc.id="desc";
desc.textContent=dat.desc;
li.appendChild(desc);
a.appendChild(li);
document.querySelector("#grid").appendChild(a);
i++;
}
});
I don't know what is causing that element to be out of place.
The reason for that is float: left; and display: inline-block; on the title and description.
The purpose of float is to make text float around an element if there is enough space (like an image or drop caps)
Based on the image it is not clear why you use display: inline-block; and float at all. As it seems that the description should always be below the title.
So remove float: left and change inline-block to block for both title and description.
Nowadays float should only be used if you really need to let float text around and element and not for other layout purposes. For layouts where float was used you should use flex or grid instead.
Additional having a as a child of ul is not valid. And a li as a child of a isn't either.
$.getJSON('https://raw.githubusercontent.com/Pinball3D/andysthings/main/registy.json',
function(data) {
//console.log(data);
var i = 0;
//console.log(data.length, i < data.length)
while (i < data.length) {
dat = data[i];
var li = document.createElement("li");
li.id = "item";
var a = document.createElement("a");
a.href = dat.url;
var img = document.createElement("img");
img.src = dat.img;
img.id = "img";
a.appendChild(img);
var title = document.createElement("div");
title.id = "title";
title.textContent = dat.name;
a.appendChild(title);
var desc = document.createElement("div");
desc.id = "desc";
desc.textContent = dat.desc;
a.appendChild(desc);
li.appendChild(a);
document.querySelector("#grid").appendChild(li);
i++;
}
});
body {
font-family: sans-serif;
}
#img {
vertical-align: top;
display: inline-block;
border-radius: 10px 10px 0px 0px;
width: 340px;
}
#title {
margin-top: 40px;
font-weight: bold;
font-size: 30px;
margin-left: 5px;
}
#desc {
margin-left: 5px;
}
#grid {
display: flex;
flex-flow: row wrap;
justify-content: space-between;
}
#grid li {
display: inline-block;
border-radius: 5px;
background-color: rgb(200,200,200);
max-width: 340px;
color: white;
}
#grid li a {
display: block;
color: inherit;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul id="grid">
</ul>
How can I append/prepend the same div created when the button is clicked to the divs in the HTML with the classes "quickAdds and "sideMenu, when the code completes it only prepends to sideMenu class div, I assume it overrides the quickAdds div because it's the last line of code. How can I fix this?
var addTaskBtn = document.querySelector("#enterBtn");
var titleInput = document.querySelector("#textField");
var taskDescription = document.querySelector("#taskDescriptionBox");
var quickAdds = document.querySelector(".quickAdds");
var sideMenu = document.querySelector(".sideMenu");
addTaskBtn.addEventListener("click", function() {
var div = document.createElement("div");
var h = document.createElement("h2");
var p = document.createElement("p");
h.textContent = titleInput.value;
p.textContent = taskDescription.value;
div.appendChild(h);
div.appendChild(p);
quickAdds.prepend(div);
sideMenu.prepend(div);
});
.sideMenuContainer {
/* display: none; */
display: block;
height: 100vw;
width: 550px;
position: absolute;
background: #343434a4;
}
.sideMenu {
display: grid;
grid-template-columns: 1fr;
width: 550px;
margin: 0;
}
.sideMenuTask {
height: auto;
width: 100%;
color: white;
margin: 0;
}
.quickAdds {
background: springgreen;
height: auto;
grid-gap: 5px;
display: grid;
grid-template-columns: repeat(4, 1fr);
border: 3px solid black;
border-radius: 3px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
-ms-border-radius: 3px;
-o-border-radius: 3px;
}
<div class="sideMenuContainer">
<div class="sideMenu">
<div class="sideMenuTask">
<h1>heading of tasks</h1>
<p>Body of tasks</p>
</div>
</div>
</div>
<div class="quickAdds">
</div>
tldr; you need to create two div elements.
When using prepend / appendChild The DOM will keep reference to the element, not value.
This means that it running the following code:
quickAdds.prepend(div);
sideMenu.prepend(div);
Will do the following:
Prepend div to quickAdds
Detach div from quickAdds
Prepend div to sideMenu
In order to prepend to both, you need to create two div elements. E.g.
const div = document.createElement('div');
const div2 = document.createElement('div');
quickAdds.prepend(div);
sideMenu.prepend(div2);
Elements are objects. When you create them they are unique, this is why when you move a node around like you are, it doesn't duplicate itself, the element node is actually moved.
We can use object.assign to duplicate an object, even an element, but the problem here is that you have several elements nested within each other. In order for them to be properly appended every single one of them has to be duplicated separately. When that's the case it's best to just create them individually or, more preferably, in a loop or function call.
When it comes to this type of situation I typically use functional programming to solve the problem, or at least mitigate the code.
This starts out with creating a simple create function that we can adjust for our needs.
function create(ele, params = {}) {
return Object.assign(document.createElement(ele), params);
}
let h2;
console.log( h2 = create("h2", { className: "blue", textContent: "hiya!" } ));
document.body.appendChild(h2);
.blue {
background: blue;
width: 100px;
height: 100px;
color: white;
}
You can extend this for use in your code. This is a simple example of how it could work, but you can also simplify this quite a great deal if you use the function properly. I won't go into that in depth because it's out of scope for this answer, but it's not difficult to imagine.
var addTaskBtn = document.querySelector("#enterBtn");
var titleInput = document.querySelector("#textField");
var taskDescription = document.querySelector("#taskDescriptionBox");
var quickAdds = document.querySelector(".quickAdds");
var sideMenu = document.querySelector(".sideMenu");
addTaskBtn.addEventListener("click", function() {
var div = create("div"), div2 = create("div");
var h = create("h2", { textContent: titleInput.value }),
h2 = create("h2", { textContent: titleInput.value });
var p = create("p", {textContent: taskDescription.value }),
p2 = create("p", {textContent: taskDescription.value });
div.appendChild(h); div2.appendChild(h2);
div.appendChild(p); div2.appendChild(p2);
quickAdds.prepend(div);
sideMenu.prepend(div2);
});
function create(ele, params = {}) {
return Object.assign(document.createElement(ele), params);
}
.sideMenuContainer {
/* display: none; */
display: block;
height: 100vw;
width: 550px;
position: absolute;
background: #343434a4;
}
.sideMenu {
display: grid;
grid-template-columns: 1fr;
width: 550px;
margin: 0;
}
.sideMenuTask {
height: auto;
width: 100%;
color: white;
margin: 0;
}
.quickAdds {
background: springgreen;
height: auto;
grid-gap: 5px;
display: grid;
grid-template-columns: repeat(4, 1fr);
border: 3px solid black;
border-radius: 3px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
-ms-border-radius: 3px;
-o-border-radius: 3px;
}
<div class="sideMenuContainer">
<div class="sideMenu">
<div class="sideMenuTask">
<h1>heading of tasks</h1>
<p>Body of tasks</p>
</div>
<button id="enterBtn">add task or whatever</button>
</div>
</div>
<div class="quickAdds">
</div>
<input type="text" id="textField">
<input type="text" id="titleInput" value="hihihhihi">
<input type="text" id="taskDescriptionBox" value="this is a description ">
I want to make a button inside auto generated block to change overflow from hidden to auto.
I created recursive responsive auto-grid in Less, css like this:
.container {
.container-fixed();
[class*='col-'] {
float: right;
width: 100%;
}
.make-grid(#container-xs);
.make-grid(#container-sm);
.make-grid(#container-md);
.make-grid(#container-lg);
}
.container-fixed(#gap: #grid-gap-width) {
margin-right: auto;
margin-left: auto;
padding-left: (#gap / 2);
padding-right: (#gap / 2);
}
.generate-columns(#container-width;
#number-cols;
#i: 1) when (#i =< #number-cols) {
.col-#{i} {
#single-width: #container-width / #number-cols - 0.5;
width: #i * #single-width; // 800px
}
.generate-columns(#container-width;
#number-cols;
#i + 1);
}
.make-grid(#container-width) {
#media(min-width: #container-width) {
width: #container-width;
.generate-columns(#container-width, #grid-c);
}
}
[class*='col-'] {
text-align: center;
overflow: hidden;
height: 250px;
background: #color-h;
display: block;
margin: 1px;
color: #color-text;
position: relative;
}
And now I have long text in HTML inside one of blocks no matter which one, eg. col-9 where is part hidden because I used overflow:hidden;.
What I would like to do is to create a button and on click to change from overflow:hidden; to overflow: auto;.
My question is how to do that, to change from hidden to auto, on click and again to return back to previous state on new click.
I tried something like this but that is not good:
Less - >
[class*='col-'] {
text-align: center;
overflow: hidden;
height: 250px;
background: #color-h;
display: block;
margin: 1px;
color: #color-text;
position: relative;
.show {
overflow: auto;
}
}
JS - >
var content = document.getElementsByClassName("[class*='col-']");
var button = document.getElementbyID("show");
button.onclick = function() {
if (content.className == "show") {
content.className= "";
button.inerHTML = "Read";
} else {
content.className="show";
button.inerHTML = "Close";
}
};
html - >
<div class="col-9">
<a id="button-show">Read</a>
<script src="js/read.js"></script>
<p> some long text ........ </p>
</div>
I hope I am clear enough, what I want to do.
<-- language: lang-javascript -->
$("#button-show").click(function(){
$(".col-9").toggleClass("show")
})
<-- -->
so whenever you click the button, it will add or remove the class show on your elements with the col-9 classnames
You should use .toggle() to toggle the contents between show and hide. Here is an example
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
$(document).ready(function(){
$("#button-show").click(function(){
$("#show").toggle();
});
});
[class*='col-'] {
text-align: center;
overflow: hidden;
height: 250px;
background: #color-h;
display: block;
margin: 1px;
color: #color-text;
position: relative;
}
#show {
overflow: auto;
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="col-9">
<a id="button-show">Read</a>
<p id="show"> some long text ........ </p>
</div>
You have multiple problems in your code.
document.getElementsByClassName returns a list of elements (A.K.A. array), so your content.className is wrong as you accesing the array className property (which is non-existant) instead of the className property of each element inside the array. You have to iterate the array and access each element individually. Also, you are not accesing by class, but by selector (There's no class [class*='col-'], but class col-1, col-2, etc...). To select with selectors you have to use querySelector, which selects one element, or querySelectorAll which selects all elements.
Also, to hide an element you don't have to change overflow. overflow is for scrollbars. You have to change the display property to display: none and also as the class show is not a child element, it needs an & character:
[class*='col-'] {
text-align: center;
[...CSS THINGYS...]
position: relative;
&.show { // Note the & before the dot
display: none;
}
}
Your code don't has any jQuery actually. Is plain JS.
Also, the best way to attach events to HTML elements is via addEventListener, so:
var content = document.querySelectorAll("[class*='col-']");
var button = document.getElementbyID("show");
button.addEventListener("click", function() {
var anyShown = false;
content.forEach(function(element) {
if (element.className == "show") {
anyShown = true;
element.className= "";
} else {
element.className="show";
}
});
if (anyShown) {
button.inerHTML = "Read";
} else {
button.inerHTML = "Close";
}
});
If you want it in a more jQuery way you can do this, which do the same as above, but way shorter:
$("#show").on("click", function() {
if ($("[class*='col-']").hasClass("show")) {
$("#show").html("Read");
} else {
$("#show").html("Close");
}
$("[class*='col-']").toggleClass("show");
});
Relevant info:
addEventListener
getelementsbyclassname
querySelector
querySelectorAll
Less "&" operator
jQuery hasClass
jQuery toggleClass
I found solution:
JQ:
$(document).ready(function(){
$("button").click(function(){
$("p3").toggle();
});
});
CSS:
[class*='col-'] {
text-align: center;
overflow:auto;
height: 250px;
background: #color-h;
margin: 1px;
color: #color-text;
position: relative;
.border-radius(10px);
p3 {
margin: 10px ;
padding: 5px;
width: 95%;
text-align: justify;
display: none;
}
}
HTML:
<div class="col-9">
<button>Read</button>
<h1>TITLE</h1>
<p>some tekst.</p>
<p3>Tekst i want to hide ....</p3>
</div>
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.
I'm quite new with css. I want hold the ul visible when hovering from parent to ul. I don't know how do it.
HTML Markup
<drop-down class="dropdown">
<span>Dropdown menu<i class="fa fa-cog"></i></span>
<ul>
<li>
Github<i class="fa fa-github"></i>
</li>
<li>
BitBucket<i class="fa fa-bitbucket"></i>
</li>
<li>
Dropbox<i class="fa fa-dropbox"></i>
</li>
<li>
Google drive<i class="fa fa-google"></i>
</li>
</ul>
</drop-down>
CSS
drop-down {
background-color: #e9e9e9;
border: 1px solid #d2c2c2;
border-radius: 2px;
display: flex;
flex-flow: column nowrap;
height: 40px;
justify-content: center;
position: relative;
width: 160px;
}
drop-down:hover { cursor: pointer; }
drop-down > span {
align-items: center;
color: #555;
display: flex;
font-family: 'segoe ui';
font-size: .9rem;
justify-content: space-between;
padding: 0px .75rem;
pointer-events: none;
}
drop-down > span > i {
color: inherit;
}
drop-down ul {
background-color: #e9e9e9;
border: 1px solid #d2c2c2;
border-radius: 2px;
box-shadow: 0px 2px 5px 1px rgba(0,0,0,.15);
display: block;
right: 10%;
list-style: none;
padding: .5rem 0;
position: absolute;
opacity: 0;
pointer-events: none;
visibility: hidden;
top: 160%;
transition: all .2s ease-out;
width: 100%;
z-index: 999;
}
drop-down ul > li {
color: #555;
display: block;
}
drop-down ul > li:hover {
background-color: #007095;
color: rgba(255,255,255,.9);
}
drop-down ul > li > a {
align-items: center;
color: inherit;
display: flex;
font-family: 'segoe ui';
font-size: .95rem;
justify-content: space-between;
padding: .5rem .75rem;
text-decoration: none;
}
drop-down ul > li > a > i {
color: inherit;
}
drop-down:focus {
outline: none;
}
drop-down:hover ul {
pointer-events: auto;
opacity: 1;
top: 120%;
visibility: visible;
}
You can see it running at this fiddle: http://jsfiddle.net/vt1y9ruo/1/
I can do it with javascript, but I don't want use it for something small.
Here's a working fiddle: http://jsfiddle.net/vt1y9ruo/8/
It works by inserting an invisible bridge between the button and the list.
drop-down:hover ul, #ulwrap:hover ul {
pointer-events: auto;
opacity: 1;
top:120%;
visibility: visible;
}
#ulwrap {
display: block;
height:0;
width:100%;
position:absolute;
}
drop-down:hover #ulwrap, #ulwrap:hover {
height:100px;
}
if you want to do this using the hover feature of css, the gap between the button and the list is what's killing you. either remove this gap or use js
on a side note there is no harm in using js for something small, this is what its used for, just make it nice and reusable
Well, pure css solution (many thanks #JBux) is a little dirty (mark up). I finally go for JS solution and for this, created a custom tag:
const helper = new Helper(); // helper functions
var ddProto = Object.create(HTMLElement.prototype);
ddProto.properties = {
list: null,
options: null,
value: null,
icon: null,
index: -1,
};
ddProto.initEvents = function() {
var self = this;
// mouse over button
this.addEventListener('mouseover', function(e) {
if(!helper.hasClass(this, 'dropdown-active'))
helper.addClass(this, 'dropdown-active');
});
// mouseleave over button
this.addEventListener('mouseleave', function(e){
var rect = this.getBoundingClientRect();
var left = e.pageX;
var bottom = e.pageY;
// if mouse is out of X axis of button and if mouse is
// out (only of top) of Y axis of button, hide ul
if(left < rect.left || left >= rect.right || bottom < rect.top) {
helper.delClass(this, 'dropdown-active');
}
});
// list loses hover
this.properties.list.addEventListener('mouseleave', function(e) {
if(helper.hasClass(self, 'dropdown-active'))
helper.delClass(self, 'dropdown-active');
});
// elements click
[].forEach.call(this.properties.options, function(e) {
e.addEventListener('click', function(event){
event.preventDefault();
// set the text of selected value to button
helper.text(self.properties.value, e.innerText);
// set the position of selected value
self.properties.index = helper.position(e.parentNode);
// set the <i> class name to the button (fontawesome)
self.properties.icon.className = this.children[0].className;
// hide ul
helper.delClass(self,'dropdown-active');
},true);
});
};
ddProto.value = function() {
return this.properties.value;
};
ddProto.index = function() {
return this.properties.index;
}
ddProto.createdCallback = function() {
this.properties.list = this.querySelector('ul');
this.properties.options = this.querySelectorAll('ul > li > a');
this.properties.value = this.querySelector('span');
this.properties.icon = this.querySelector('span > i');
this.initEvents();
};
document.registerElement('drop-down', {prototype: ddProto});
Working fiddle: http://jsfiddle.net/m2dtmr24/2/
Thank you so much.
The thing you could check is the + selector (more here)
In short it lets you add styles to elements right next to each other. The actual css might look something like this:
.dropdown{
display: none;
}
.button:hover+.dropdown{
display: block;
}
This will only work when .dropdown is directly below .button in the DOM
The animation might be harder, but you could achieve something similar by for example using transition on opacity, and toggle opacity instead of display