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>
Related
I have this basic HTML that represents a button, I want to attach a click event, but this button is appendend dinamically to the container through a an ajax function, so because this is not present initialy in the DOM, I'm attaching the event to the container in this way
document.querySelector('#container').addEventListener('click', (e) => {
var t = e.target.classList;
if( t.contains('button-outer') ) console.log(e.target);
});
#container {
width: 100%;
height: 100%;
position: relative;
text-align: center;
}
.button-outer {
padding: 15px;
background-color: orange;
display: inline-block;
margin: 0 auto;
text-align: center;
}
.button-inner{
font-weight: bold;
cursor: pointer;
font-size: 75px;
}
<div id="container">
<div class="button-outer">
<div class="button-inner">BUTTON</div>
</div>
</div>
This works, but obviously only when I'm clicking on on the padding part of the outer div. To fix this I have to change the if statement in a way that it will contains the inner part too, like this:
document.querySelector('#container').addEventListener('click', (e) => {
var t = e.target.classList;
if( t.contains('button-outer') || t.contains('button-inner')) console.log(e.target);
});
I think that this is a little uncovenient, because sometimes the inner part could have other classes, or could be an icon, a link, it is a little difficoult to create specific statements each time, so question is:
How can I propagate the event starting from outer to all inner elements when the button is dinamically appended?
You should attach your event handler when the button is created, in your ajax function.
But if you need to do it the way you are doing it, you can use closest(), it will traverse all of the target's parents until it finds your query.
document.querySelector('#container').addEventListener('click', (e) => {
var t = e.target;
if(t.closest('.button-outer')) console.log(e.target);
});
#container {
width: 100%;
height: 100%;
position: relative;
text-align: center;
}
.button-outer {
padding: 15px;
background-color: orange;
display: inline-block;
margin: 0 auto;
text-align: center;
}
.button-inner{
font-weight: bold;
cursor: pointer;
font-size: 75px;
}
<div id="container">
<div class="button-outer">
<div class="button-inner">BUTTON</div>
</div>
</div>
I want to make toggle nav but unfortunately not working
function navtoggle(){
document.getElementsByClassName('sidebar').classList.toggle('active');
}
</script>
.sidebar.active {
width:0px;
}
document.getElementsByClassName('sidebar').classList is a HTMLCollection, you must loop through it or pick a specific index
function navtoggle(){
document.getElementsByClassName('sidebar')[0].classList.toggle('active');
}
.active {
color: red;
}
<div class="sidebar">Hello</div>
<button onClick="navtoggle();">Click Me</button>
document.getElementsByClassName() will return a HTMLCollection instead of the single sidebar element you're trying to target. Because classList isn't a property of HTMLCollection, you'll simply get a TypeError when trying to toggle a class on this collection.
If you are sure there is only one element with .sidebar on the page, or that the sidebar you're trying to target is the first in the DOM, you could use document.getElementsByClassName('sidebar')[0]. An alternative approach would be to use document.querySelector('.sidebar'), where you can also pass a more specific selector, such as document.querySelector('aside.sidebar'), to ensure you've selected the right element. For example:
function navtoggle() {
document.querySelector('aside.sidebar').classList.toggle('active');
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: sans-serif;
}
body {
width: 100vw;
height: 100vh;
}
.sidebar {
background: #CCC;
width: 15%;
height: 100%;
padding: 1rem;
overflow: hidden;
}
.sidebar.active {
width: 0;
padding: 0;
}
main {
position: absolute;
right: 0;
top: 0;
width: 85%;
height: 100%;
padding: 1rem;
}
button {
padding: .5rem;
margin-top: .5rem;
}
<aside class="sidebar">
This is the sidebar
</aside>
<main>
This is not the sidebar <br>
<button onclick="navtoggle()">Toggle the sidebar</button>
</main>
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
I am trying to get the node.firstChild of the #root element, after generating content within the container. I expect it to be the first div, because when I look at the elements in the dev console, that's the first child that I see. I am not sure where this #text is coming from, or what it means even.
Please help me understand:
What #text is (obviously it's some type of text, but I don't see it)
Why it's showing up instead of the firstChild of my container which should actually be div.each-result
It should be noted that I am running this code in CodePen
I am also aware I can also use Node.firstElementChild, but I want to understand what's going wrong currently.
const leftArrow = document.querySelector('#left-arrow');
const rightArrow = document.querySelector('#right-arrow');
const rootDiv = document.querySelector('#root');
const generateButton = document.querySelector("#button-generate");
//This code basically generates the content within the div
generateButton.addEventListener("click", () => {
for (let i = 0; i < 10; i++) {
const newDiv = document.createElement("div");
newDiv.classList.add("each-result");
newDiv.appendChild(addImg("https://uk.usembassy.gov/wp-content/uploads/sites/16/please_read_icon_150x150.jpg"));
rootDiv.appendChild(newDiv);
}
console.log(rootDiv.firstChild);
});
//These enable the arrow to scroll through the dynamically generated content
leftArrow.addEventListener('click', () => {
//use
});
rightArrow.addEventListener('click', () => {
alert("right arrow works");
});
//Simple function to create and image element with the src attribute set in one line
function addImg(url) {
const newImg = document.createElement("img");
newImg.setAttribute("src", url);
return newImg;
}
html, body {
height: 100%;
}
button {
position: relative;
z-index: 1
width: auto;
height: 50px;
}
.container {
display: flex;
justify-content: center;
position: relative;
top: 15%;
z-index: 0
}
.result-container {
display: flex;
align-items: center;
border: 1px solid black;
width: 80%;
height: 200px;
position: relative;
flex-flow: row no-wrap;
overflow: hidden;
}
.each-result {
height: 150px;
width: 150px;
border: 3px dotted red;
margin: 1%;
}
img {
height: 100%;
width: auto;
}
.nav-arrows {
display: flex;
justify-content: space-between;
width: 100%;
height: auto;
position: absolute;
background: clear;
pointer-events: none;
}
#left-arrow, #right-arrow {
pointer-events: auto;
}
<script src="https://use.fontawesome.com/releases/v5.0.6/js/all.js"></script>
<div class="container">
<div class="nav-arrows">
<button id="left-arrow"><i class="fas fa-arrow-alt-circle-left"></i>
</button>
<button id="right-arrow"> <i class="fas fa-arrow-alt-circle-right"></i>
</button>
</div>
<div id="root" class="result-container">
</div>
</div>
<button id="button-generate">Generate Content</button>
Look at the first example here: Node.firstChild
In the above, the console will show '#text' because a text node is
inserted to maintain the whitespace between the end of the opening <p>
and <span> tags. Any whitespace will create a #text node, from a
single space to multiple spaces, returns, tabs, and so on.
Another #text node is inserted between the closing </span> and
</p> tags.
If this whitespace is removed from the source, the #text nodes are not
inserted and the span element becomes the paragraph's first child.
As you suggested yourself, ParentNode.firstElementChild is the best way to go in this case.
Consider we're going to create a page containing a textarea for typing an article. the size of textarea is set to an A5 paper size. For long Texts when User types and completes the first textarea, It's required to add another textarea following to the first textarea to allow user continue typing in next page(something like MS word).
What's your suggestion?
.A5 {
width: 148mm;
padding: 1cm;
margin: 1px;
box-sizing: border-box;
height: 210mm;
border: solid 1px;
resize: none;
display: block;
}
#page {
size: A5;
margin: 0;
padding: 0;
}
#media print {
.A5 {
visibility: visible;
position: fixed;
top:0;
left:0;
z-index: 99;
border:none;
}
body> :not(.A5){
color: red;
display:none;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<h1>
Paper Print Test
</h1>
<input type="button" value="print" onclick="window.print()"/>
<textarea class="A5">
Article Text
</textarea>
updated, add handle for delete in second page
I guess this is you want, detect scroll using clientHeight and scrollHeight. And a lot more is left for you
1.backspace on empty page or backspace before first char
2.insert/delete in already full pages
3.cusor move between pages
$('body').on('keyup', '.A5', function(e) {
if ($(this)[0].clientHeight < $(this)[0].scrollHeight) {
eatBackAndNew(this)
} else if (e.keyCode == 8 || e.keyCode == 46) {
//backspace=8,del=46
if ($(this).val() === '' && !$(this).attr('data-first')) {
$(this).prev().focus()
$(this).remove()
}
}
})
//this can be more complicated if user paste
function eatBackAndNew(textarea) {
let str = $(textarea).val()
let newStr = str.substr(str.length - 1, 1)
$(textarea).val(str.substr(0, str.length - 1))
let $newTextarea = $(`<textarea class="A5">${newStr}</textarea>`)
$('.js-container').append($newTextarea)
$newTextarea.focus()
}
.A5 {
width: 148mm;
padding: 1cm;
margin: 1px;
box-sizing: border-box;
height: 210mm;
border: solid 1px;
resize: none;
display: block;
}
#page {
size: A5;
margin: 0;
padding: 0;
}
#media print {
.A5 {
visibility: visible;
position: fixed;
top: 0;
left: 0;
z-index: 99;
border: none;
}
body> :not(.A5) {
color: red;
display: none;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<h1>
Paper Print Test
</h1>
<input type="button" value="print" onclick="window.print()" />
<div class="js-container"><textarea class="A5" data-first="true">
Article Text
</textarea>
</div>
Yes it is possible, see the link(please cheek carefully).
This is preview link: https://codepen.io/ziruhel/pen/VrzpqG
There might have more related issue. I think you can solved it, if you need help, please let me know.
If You want it with print preview, Then this solution is for you.
Preview link: https://codepen.io/ziruhel/pen/ZaJpNe
HTML:
<h1>
Paper Print Test
</h1>
<input type="button" value="print" onclick="window.print()"/>
<div class="A5-print">g</div>
<textarea class="A5">
Article Text
</textarea>
CSS:
.A5,.A5-print {
width: 148mm;
padding: 1cm;
margin: 1px;
box-sizing: border-box;
}
.A5{
border: solid 1px;
height: 210mm;
resize: none;
display: block;
}
.A5-print{
display: none;
}
#page {
size: A5;
margin: 0;
padding: 0;
}
#media print {
.A5-print{
visibility: visible;
z-index: 99;
display: block;
page-break-after: always !important;
overflow: visible;
white-space: pre;
white-space: pre-wrap;
}
body :not(.A5-print){
display:none;
}
*{
page-break-after: always !important;
}
}
jQuery:
jQuery(function($){
function copy_to_print_helper(){
$('.A5-print').text($('textarea').val());
}
$('textarea').bind('keydown keyup keypress cut copy past blur change', function(){
copy_to_print_helper(); // consider debouncing this to avoid slowdowns!
});
copy_to_print_helper(); // on initial page load
});
Preview link: https://codepen.io/ziruhel/pen/ZaJpNe
It might be better to avoid using textareas for this one and just use DIVs. It's a lot more flexible. You can create a pages DIV and simply add an 'absolute' positioned line every certain amount of distance, because you know the exact dimensions.
I've been looking at how Word online does this. They seem to use a DIV approach as well, but more complicated.