im trying to make static grid with a button that can change number of boxes in it (from 16x16 to 64x64 and anything between). Grid is 40rem x 40rem, when i try to change manually number of boxes in makeGrid() function it works fine up to 20 (boxes change size accordingly), but anything above 20 stays the same size and gets cutoff from my grid. If there is no grid css overflow property stated, grid width change depending on number of boxes but boxes themself won't shrink
my code:
size button is not working yet, grid size need to be changed mannualy in makeGrid function
const grid = document.getElementById('grid');
const size = document.getElementById('size');
const eraser = document.getElementById('eraser');
const color = document.getElementById('color');
const gridBorder = document.getElementById('grid-borders');
const clear = document.getElementById('clear');
// grid
function makeGrid(number) {
number = number || 16;
let cellWidth = 40 / number + 'rem';
let cellHeight = 40 / number + 'rem';
grid.style.gridTemplateColumns = `repeat( ${number}, 1fr)`;
grid.style.gridTemplateRows = `repeat(${number}, 1fr)`;
for (let i = 0; i < number * number; i++) {
let cell = document.createElement('div');
grid.appendChild(cell).id = 'box';
cell.classList.add('border');
cell.classList.add('box');
cell.style.backgroundColor = 'white';
cell.style.width = cellWidth;
cell.style.height = cellHeight;
}
size.textContent = `${number} x ${number}`;
}
makeGrid();
// drawing on hover
color.addEventListener('click', function () {
grid.addEventListener('mouseover', function (e) {
e.target !== grid ? (e.target.style.backgroundColor = 'black') : null;
});
});
function changeColor(event) {
event.target.style.backgroundColor = 'black';
}
// erase functionality
eraser.addEventListener('click', function () {
grid.addEventListener('mouseover', function (e) {
e.target !== grid ? (e.target.style.backgroundColor = 'white') : null;
});
});
// grid borders
const allBoxes = document.querySelectorAll('.box');
gridBorder.addEventListener('click', function () {
allBoxes.forEach((box) => {
box.classList.toggle('no-border');
box.classList.toggle('border');
});
});
// clear button
clear.addEventListener('click', function () {
allBoxes.forEach((box) => {
box.style.backgroundColor = 'white';
});
});
// size button
// size.addEventListener('click', function () {
// let number = prompt(`Enter grid size less or equal to 100`);
// if (number !== Number.isInteger()) {
// return;
// } else if (number > 100) {
// number = prompt(`Enter grid size greater or equal to 100`);
// }
// });
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
height: 100vh;
background-color: aquamarine;
}
#grid {
display: grid;
justify-content: center;
border: 1px solid #ccc;
width: 40rem;
height: 40rem;
min-width: 0;
min-height: 0;
overflow: hidden;
}
.box {
padding: 1em;
}
#title {
display: flex;
align-items: flex-end;
justify-content: center;
height: 180px;
}
#container {
display: flex;
height: 60%;
width: 1259px;
align-items: flex-start;
justify-content: flex-end;
gap: 20px;
padding-top: 20px;
}
#menu {
display: flex;
flex-direction: column;
gap: 10px;
}
.border {
outline: 1px solid black;
}
.no-border {
outline: none;
}
.black-bg {
background: black;
}
.white-bg {
background: white;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Etch-a-Sketch</title>
<link rel="stylesheet" href="style.css" />
<script src="script.js" defer></script>
</head>
<body>
<div id="title">
<h1>Etch-a-Sketch</h1>
</div>
<main id="container">
<div id="menu">
<button id="size"></button>
<button id="color">Color</button>
<button id="eraser">Eraser</button>
<button id="clear">Clear</button>
<button id="grid-borders">Grid Borders</button>
</div>
<div id="grid"></div>
</main>
</body>
</html>
"Why won't my grid cells go below 32px?" - have you checked your padding (hint: 32px is exactly equal to 2 * 16px which in turn is exactly equal to your padding of 1em with most browsers implementing a default font-size of 16px). –
David Thomas
box padding was set to 1em which caused my problem, after deleting it my grid worked as intended
On mobile, it's a common UI pattern to have a scrollable element inside a draggable element. When you reach the end of the scrollable element, you start dragging the outer element. E.g. in this GIF (https://media.giphy.com/media/9MJgBkoZfqA7jRdQop/giphy.gif), after scrolling to the top, if you continuing scrolling, it'll drag the subreddits menu.
I want to implement a similar pattern using JS/CSS. To do this, I need to detect if users continue scrolling after reaching the end. Is this possible? If so, is it possible to determine how much they scroll after reaching the end?
window.onscroll = function(element) {
if ((window.innerHeight + window.pageYOffset) >= document.body.offsetHeight) {
alert("you're at the bottom of the page");
}
};
Using element parameter to know the current exact x y where mouse is now at to calculate more and some how much was scrolled
Javascript: How to detect if browser window is scrolled to bottom?
If You need to keep track of the user activity after the bottom (or the top) of the page has been reached, beside the scroll event, You need to track the the wheel event. Moreover, on mobile, You need to track also touchstart and touchmove events.
Not all these events are normalized across browsers, so I did my own normalization function, which is more or less something like this:
var compulsivity = Math.log2(Math.max(scrollAmount, 0.01) * wheelAmount);
Below is a complete playground. You can test it in Chrome using the Mobile View of the Developer Tools, or in other browsers using the TouchEmulator.
function Tracker(page) {
this.page = page;
this.moveUp = 0;
this.moveDown = 0;
this.startTouches = {};
this.moveTouches = {};
this.lastScrollY = 0;
this.monitor = {};
this.startThreshold = 160;
this.moveThreshold = 10;
this.iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
this.pullToRefresh = window.chrome || navigator.userAgent.match('CriOS');
this.amplitude = 16 / Math.log(2);
this.page.ownerDocument.addEventListener( 'onwheel' in document ? 'wheel' : 'onmousewheel' in document ? 'mousewheel' : 'DOMMouseScroll', this, { passive: true } );
/* The basic scroll event cannot be canceled, so it does not need to be set passive.*/
this.page.ownerDocument.addEventListener('scroll', this);
this.page.addEventListener('touchstart', this, { passive: true });
/* Maybe we need to cancel pullToRefresh */
this.page.addEventListener('touchmove', this, { passive: false });
return this;
}
Tracker.prototype.handleEvent = function (e) { /* handleEvent is built-in */
var winHeight = (this.iOS ? document.documentElement.clientHeight : window.innerHeight) | 0,
currScrollY = window.pageYOffset | 0,
amountScrollY = (this.lastScrollY - currScrollY) | 0,
elHeight = this.page.offsetHeight | 0,
elTop = -currScrollY, elBottom = winHeight - elHeight + currScrollY,
isTop = elTop >= 0, isBottom = elBottom >= 0;
switch (e.type) {
case 'wheel':
case 'onmousewheel':
case 'mousewheel':
case 'DOMMouseScroll':
var wheelDelta = e.wheelDelta ? e.wheelDelta : e.deltaY ? -e.deltaY : -e.detail,
wheelDir = (wheelDelta > 0) - (wheelDelta < 0),
wheelUp = wheelDir < 0, wheelDown = wheelDir > 0,
wheelAmount = 100 * wheelDir;
if (isTop && wheelDown) {
this.moveUp++;
this.moveDown = 0;
} else if (isBottom && wheelUp) {
this.moveUp = 0;
this.moveDown++;
} else {
this.moveUp = 0;
this.moveDown = 0;
}
var compulsivity = this.amplitude * Math.log(Math.max(this.moveUp, this.moveDown, 0.01) * wheelAmount* wheelDir);
this.monitor[e.type].track(wheelAmount, compulsivity);
break;
case 'scroll':
/* end of scroll event for iOS, start/end of scroll event for other browsers */
this.lastScrollY = currScrollY;
this.monitor[e.type].track(amountScrollY, 0);
break;
case 'touchstart':
var touches = [].slice.call(e.touches), i = touches.length;
while (i--) {
var touch = touches[i], id = touch.identifier;
this.startTouches[id] = touch;
this.moveTouches[id] = touch;
}
break;
case 'touchmove':
var touches = [].slice.call(e.touches), i = touches.length,
currTouches = {},
swipeUp = false, swipeDown = false,
currMoveY = 0, totalMoveY = 0;
while (i--) {
var touch = touches[i], id = touch.identifier;
currTouches[id] = touch;
if (id in this.moveTouches) {
currMoveY = this.moveTouches[id].screenY - touch.screenY;
}
if (id in this.startTouches) {
totalMoveY = this.startTouches[id].screenY - touch.screenY;
}
swipeUp = currMoveY > 0 || totalMoveY > 0;
swipeDown = currMoveY < 0 || totalMoveY < 0;
if (this.pullToRefresh && isTop && swipeDown && e.cancelable) {
e.preventDefault();
console.log('Reload prevented');
}
}
this.moveTouches = currTouches;
var moveDir = (totalMoveY > 0) - (totalMoveY < 0),
longSwipe = moveDir * totalMoveY > this.startThreshold,
shortSwipe = moveDir * totalMoveY > this.moveThreshold,
realSwipe = longSwipe || shortSwipe;
if (isTop && swipeDown) {
if (realSwipe) this.moveUp++;
this.moveDown = 0;
} else if (isBottom && swipeUp) {
this.moveUp = 0;
if (realSwipe) this.moveDown++;
} else {
this.moveUp = 0;
this.moveDown = 0;
}
var compulsivity = this.amplitude * Math.log(Math.max(this.moveUp, this.moveDown, 0.01) * moveDir * totalMoveY);
this.monitor[e.type].track(currMoveY, compulsivity);
break;
}
};
function Monitor(events) {
this.ctx = null;
this.cont = null;
this.events = events;
this.values = [];
this.average = 0;
this.lastDrawTime = 0;
this.inertiaDuration = 200;
return this;
}
Monitor.prototype.showOn = function (container) {
var cv = document.createElement('canvas');
this.ctx = cv.getContext('2d');
this.cont = document.getElementById(container);
cv.width = this.cont.offsetWidth;
cv.height = this.cont.offsetHeight;
cv.style.top = 0;
cv.style.left = 0;
cv.style.zIndex = -1;
cv.style.position = 'absolute';
cv.style.backgroundColor = '#000';
this.cont.appendChild(cv);
var self = this;
window.addEventListener('resize', function () {
var cv = self.ctx.canvas, cont = self.cont;
cv.width = cont.offsetWidth;
cv.height = cont.offsetHeight;
});
return this;
};
Monitor.prototype.track = function (value, average) {
this.average = average;
if (this.values.push(value) > this.ctx.canvas.width) this.values.shift();
if (value) this.lastDrawTime = new Date().getTime();
};
Monitor.prototype.draw = function () {
if (this.ctx) {
var cv = this.ctx.canvas, w = cv.width, h = cv.height;
var i = this.values.length, x = w | 0, y = (0.5 * h) | 0;
cv.style.backgroundColor = 'rgb(' + this.average + ', 0, 0)';
this.ctx.clearRect(0, 0, w, h);
this.ctx.strokeStyle = '#00ffff';
this.ctx.lineWidth = 1;
this.ctx.beginPath();
while (i--) {
x -= 4;
if (x < 0) break;
this.ctx.moveTo(x, y);
this.ctx.lineTo(x + 1, y);
this.ctx.lineTo(x + 1, y - this.values[i]);
}
this.ctx.stroke();
var elapsed = new Date().getTime() - this.lastDrawTime;
/* cool down */
this.average = this.average > 0 ? (this.average * 0.9) | 0 : 0;
if (elapsed > this.inertiaDuration) {
this.track(0, this.average);
}
}
var self = this;
setTimeout(function () {
self.draw();
}, 100);
};
Monitor.prototype.connectTo = function (tracker) {
var events = this.events.split(' '), i = events.length;
while (i--) {
tracker.monitor[events[i]] = this;
}
this.draw();
return this;
};
function loadSomeData(target) {
$.ajax({
url: 'https://jsonplaceholder.typicode.com/users',
method: 'GET',
crossDomain: true,
dataType: 'json',
success: function (users) {
var html = '', $ul = $(target).find('ul');
$.each(users, function (i, user) {
var item = '<li><a class="ui-alt-icon ui-nodisc-icon">';
item += '<h2>' + user.name + '</h2>';
item += '<p><strong>' + user.company.name + '</strong></p>';
item += '<p>' + user.address.zipcode + ', ' + user.address.city + '</p>';
item += '<p>' + user.phone + '</p>';
item += '<p>' + user.email + '</p>';
item += '<p class="ui-body-inherit ui-li-aside ui-li-count"><strong>' + user.id + '</strong></p>';
item += '</a></li>';
html += item;
});
$ul.append(html).listview('refresh');
},
});
}
$(document)
.on('pagecreate', '#page-list', function (e) {
$("[data-role='header'], [data-role='footer']").toolbar({ theme: 'a', position: 'fixed', tapToggle: false });
loadSomeData(e.target);
})
.on('pageshow', '#page-list', function (e, ui) {
var tracker = $.data(this, 'mobile-page', new Tracker(this));
new Monitor('touchstart touchmove').connectTo(tracker).showOn('header');
new Monitor('scroll wheel mousewheel DOMMouseScroll').connectTo(tracker).showOn('footer');
});
.ui-page {
touch-action: none;
}
h1, h2, h3, h4, h5, h6, p {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* JQM no frills */
.ui-btn,
.ui-title,
.ui-btn:hover,
.ui-btn:focus,
.ui-btn:active,
.ui-btn:visited {
text-shadow: none !important;
}
* {
-webkit-box-shadow: none !important;
-moz-box-shadow: none !important;
box-shadow: none !important;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Compulsivity</title>
<meta name="description" content="Compulsivity" />
<meta name="HandheldFriendly" content="True" />
<meta name="MobileOptimized" content="320" />
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, width=device-width, minimal-ui shrink-to-fit=no" />
<meta http-equiv="cleartype" content="on" />
<!-- Add to homescreen for Chrome on Android -->
<meta name="mobile-web-app-capable" content="yes" />
<!-- For iOS web apps. Delete if not needed. -->
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="apple-mobile-web-app-title" content="Compulsivity" />
<link rel="stylesheet" href="https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css" />
<!--
<script type="application/javascript" src="lib/touch-emulator.js"></script>
<script> TouchEmulator(); </script>
-->
<script type="application/javascript" src="https://cdn.jsdelivr.net/npm/jquery#2.2.4/dist/jquery.min.js"></script>
<script type="application/javascript" src="https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
</head>
<body>
<div id="header" data-role="header"><h4 style="color: #fff">Compulsivity</h4></div>
<div id="page-list" data-role="page">
<div data-role="content" role="main">
<ul data-role="listview" data-filter="true" data-inset="true"></ul>
</div>
</div>
<div id="footer" data-role="footer"><h4 style="color: #fff">Scroll</h4></div>
</body>
</html>
Among others, You need to be aware also of the pull-to-refresh and inertia (or momentum) of the smooth scroll behaviors.
Please, try to scroll or to swipe and look how the events are tracked: either the top bar or bottom bar will change color to display the user activity after reaching the bottom or the top respectively of the page.
JavaScript:
// get the button
var theBtn = document.getElementById('theBtn');
// get the box
var theBox = document.getElementById('theBox');
// add event to the button on click show/hide(toggle) the box
theBtn.addEventListener('click', () => {
theBox.classList.toggle('active');
});
// when scrolling on the box
theBox.onscroll = function(){
// get the top of the div
var theBoxTop = theBox.scrollTop;
if(theBoxTop <= 0){
// when it reaches 0 or less, hide the box. It'll toggle the class, since it's "show" will "hide"
theBox.classList.toggle('active');
}
};
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-size: 10px;
font-family: 'Arial', sans-serif;
height: 1500px;
}
html {
scroll-behavior: smooth;
}
ul {
list-style-type: none;
}
#theBox ul li {
border: 1px solid;
height: 100px;
}
#navbar-bottom {
height: 100px;
width: 100%;
background: rgb(90, 111, 143);
position: fixed;
bottom: 0;
left: 0;
right: 0;
box-shadow: 0 0 2px 2px rgba(90, 111, 143, 0.562);
display: flex;
justify-content: space-around;
align-items: center;
}
#theBox {
background-color: red;
height: 350px;
width: 100%;
position: fixed;
bottom: 0;
transform: translateY(100%);
transition: all 0.3s;
overflow-y: scroll;
}
#theBox.active{
transform: translateY(0);
}
.myBtns {
width: 50px;
height: 50px;
border-radius: 50%;
border: none;
position: relative;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
cursor: pointer;
}
.myBtns span {
height: 3px;
width: 30px;
background-color: black;
margin: 3px 0;
}
<main role="main">
<div id="theBox">
<ul>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
</ul>
</div>
<div id="navbar-bottom">
<button class="myBtns"></button>
<button class="myBtns" id="theBtn">
<span></span>
<span></span>
<span></span>
</button>
<button class="myBtns"></button>
</div>
</main>
jQuery:
// add event to the button on click show/hide(toggle) the box
$('#theBtn').click(function(){
$('#theBox').toggleClass('active');
});
// when scrolling on the box
$('#theBox').scroll(function () {
// get the top of the div
var theBoxTop = $('#theBox').scrollTop();
// when it reaches 0 or less, hide the box. It'll toggle the class, since it's "show" will "hide"
if(theBoxTop <= 0){
$('#theBox').toggleClass('active');
}
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-size: 10px;
font-family: 'Arial', sans-serif;
height: 1500px;
}
html {
scroll-behavior: smooth;
}
ul {
list-style-type: none;
}
#theBox ul li {
border: 1px solid;
height: 100px;
}
#navbar-bottom {
height: 100px;
width: 100%;
background: rgb(90, 111, 143);
position: fixed;
bottom: 0;
left: 0;
right: 0;
box-shadow: 0 0 2px 2px rgba(90, 111, 143, 0.562);
display: flex;
justify-content: space-around;
align-items: center;
}
#theBox {
background-color: red;
height: 350px;
width: 100%;
position: fixed;
bottom: 0;
transform: translateY(100%);
transition: all 0.3s;
overflow-y: scroll;
}
#theBox.active{
transform: translateY(0);
}
.myBtns {
width: 50px;
height: 50px;
border-radius: 50%;
border: none;
position: relative;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
cursor: pointer;
}
.myBtns span {
height: 3px;
width: 30px;
background-color: black;
margin: 3px 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<main role="main">
<div id="theBox">
<ul>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
</ul>
</div>
<div id="navbar-bottom">
<button class="myBtns"></button>
<button class="myBtns" id="theBtn">
<span></span>
<span></span>
<span></span>
</button>
<button class="myBtns"></button>
</div>
</main>
window.onscroll = function(ev) {
if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) {
alert("you are at the bottom of the page");
}
};
Link to demo: http://jsfiddle.net/5xpoe4yg/
There are two solutions for this. One is for touch devices and second for devices using mouse.
Using Wheel event
If target is a mouse device, then we will use following method:
document.onwheel = event => ScrollAction(event);
For more info on wheel event, please visit this link.
Touch Devices
If target is a touch device then following method will be useful:
document.ontouchcancel = event => TouchInterrupt(event);
document.ontouchend = event => FingerRemoved(event);
document.ontouchmove = event => FingerDragged(event);
document.ontouchstart = event => FingerPlaced(event);
For more info on touch events, please visit this link.
I think your problem fully is solved by this solution.
Your specific question is solveable by listening to the wheel event, although the result is not terribly precise. The wheel event often fires before the scroll event so this example will sometimes log negative scroll value on the first scroll up from the bottom of the page:
const content = document.querySelector('.content');
for (let i = 0; i < 50; i++) {
const p = document.createElement('p');
p.textContent = 'Content';
content.append(p);
};
content.addEventListener('wheel', e => {
const atBottom = content.scrollHeight - content.scrollTop === content.clientHeight;
if (atBottom) console.log(e.deltaY);
});
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
height: 100vh;
width: 100%;
}
.content {
overflow-y: scroll;
height: 100%;
}
<div class="content"></div>
As others have suggested, a better approach for your use case might instead be to have an overlay which you can trigger on click/touch and then scroll into view. One issue you might run into is that deeply nested scroll on web browsers can get real ugly real fast, without resorting to pure JS solutions which also have their own performance issues.
This is a popup that, when clicked on, opens and enables you to scroll. When it gets to the top of the page, it's header sticks.
var navbar = document.querySelector('.navbar'),
navheader = document.querySelector('.navheader');
// Toggle navbar
navheader.addEventListener('click', e => {
navbar.classList.toggle('open');
if (!navbar.classList.contains('open')) {
navbar.style.overflow = 'hidden';
document.body.style.overflow = '';
navbar.scrollTop = 0;
stickTop = false;
navbar.classList.remove('sticky');
navbar.style.top = '';
navbar.style.transition = '.2s';
setTimeout(() => {
navbar.style.transition = '';
}, 200);
}
else {
navbar.style.overflow = 'overlay';
navbar.style.transition = '.2s';
setTimeout(() => {
navbar.style.transition = '';
}, 200);
}
})
var prevtop = 0;
var stickTop = false;
// Add scroll listener
navbar.addEventListener('scroll', e => {
// If navbar is open
if (navbar.classList.contains('open')) {
if (!stickTop) {
navbar.style.top = navbar.getBoundingClientRect().top - navbar.scrollTop + 'px';
}
if ((window.innerHeight - navbar.getBoundingClientRect().bottom) >= 0) {
document.body.style.overflow = 'hidden';
navbar.style.overflow = 'auto';
navbar.style.top = 0;
navbar.classList.add('sticky');
stickTop = true;
}
if (navbar.scrollTop == 0) {
navbar.classList.remove('open');
navbar.style.overflow = 'hidden';
document.body.style.overflow = '';
stickTop = false;
navbar.classList.remove('sticky');
navbar.style.top = '';
navbar.style.transition = '.2s';
setTimeout(() => {
navbar.style.transition = '';
}, 200);
}
}
})
body {
font-family: sans-serif;
}
.navbar {
position: fixed;
top: calc(100vh - 50px);
height: 100vh;
left: 0;
width: 100%;
overflow: hidden;
}
.navbar.open {
top: 50vh;
}
.navcontent {
background: black;
width: 100%;
color: white;
}
.navcontent p {
margin: 0;
}
.navheader {
height: 50px;
width: 100%;
background: lightblue;
cursor: pointer;
top: 0;
position: sticky;
display: flex;
justify-content: center;
z-index: 1;
}
.navheader::before {
width: 50px;
height: 3px;
margin-top: 10px;
background: white;
border-radius: 3px;
content: '';
}
<div class="navbar">
<div class="navheader"></div>
<div class="navcontent"><p>S</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>E</p></div>
</div>
<div class="content">
<p>S</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>E</p>
</div>
I'm new to Cordova, and I'm trying to build an android app
I copied my files into www file and replaced its files with mine
and build apk but when running it on my phone the first two buttons only are working (Equal weight / Unequal weight) while the (add more/ reset/ result buttons ) are not, but when I'm trying to check my code as a web page it works perfectly, as you might see in the snippet
I also tried to keep the cordova.js file and the index.js with my app.js but it didn't work
I tried to add cordova.js and index.js (the files that created automatically when creating a project with Cordova) but it didn't work
//declare the main buttons and divs
const btn1 = document.querySelector('.eqbtn'),
btn2 = document.querySelector('.uneqbtn'),
div1 = document.querySelector('.eqdiv'),
div2 = document.querySelector('.wrap-sub')
div_2 = document.querySelector('.uneqdiv');
//add event listeners to the main buttons
btn1.addEventListener('click', equalFunc);
btn2.addEventListener('click', unequalFunc);
//switch between two keys
function equalFunc() {
togFunc(div1, div_2, btn1, btn2);
resetFunc('.eqdiv', div1);
}
function unequalFunc() {
togFunc(div_2, div1, btn2, btn1);
resetFunc('.wrap-sub', div2);
}
function togFunc(one, two, a, b) {
a.classList.add('active');
b.classList.remove('active');
one.style.display = 'block';
two.style.display = 'none';
}
//declare the equal addmore button
const btnplus = document.querySelector('.eqmore');
const unbtnplus = document.querySelector('.un-eqmore');
//fire when addmore button get fired
btnplus.addEventListener('click', () => {
if (checkIn('.eqdiv') === false) {
errIn();
} else {
creatIn(div1);
}
});
//fire when uneqaddmore get clicked
unbtnplus.addEventListener('click', () => {
if (checkIn('.wrap-sub') === false) {
errIn();
} else {
creatIn(div2);
}
})
//check the value of the inputs
function checkIn(nav) {
let bool;
document.querySelectorAll(`${nav} input`).forEach((elem) => {
if (elem.value === '' || elem.value < 0) {
elem.style.borderColor = '#f86868';
bool = false;
} else {
elem.style.borderColor = '#D1D1D1';
}
});
return bool;
}
//create the input and assign it's value
function creatIn(dnav) {
if (dnav === div1) {
const dive = document.createElement('div');
dive.innerHTML = `<input type ='number'/>`;
dnav.insertBefore(dive, btnplus);
} else {
const sp1 = document.createElement('div'),
sp2 = document.createElement('div');
sp1.innerHTML = `<input type ='number'>`;
sp2.innerHTML = `<input type ='number'>`;
document.querySelector('.div2-1').appendChild(sp2);
document.querySelector('.div2-2').appendChild(sp1);
}
}
//fire an error if the user insert wrong value
function errIn() {
const errdiv = document.createElement('div');
errdiv.innerHTML = `Please fill inputs with positive values`;
errdiv.classList.add('errdiv');
document.querySelector('.wrap').insertAdjacentElement('beforeend', errdiv);
setTimeout(() => errdiv.remove(), 1800);
}
//fire when the reset button get clicked
function resetFunc(x, y) {
document.querySelectorAll(`${x} input`).forEach(reset => {
reset.remove();
});
document.querySelector('#output').innerHTML = '';
creatIn(y);
}
//reset all the inputs
document.querySelector('.reset').addEventListener('click', () => resetFunc('.eqdiv', div1));
document.querySelector('.un-reset').addEventListener('click', () => resetFunc('.wrap-sub', div2));
//fire when result button get fired
document.querySelector('.result').addEventListener('click', resFunc);
document.querySelector('.un-result').addEventListener('click', unresFunc);
//declare result function when result button get clicked
function resFunc() {
if (checkIn('.eqdiv') === false) {
errIn();
} else {
let arr = [];
document.querySelectorAll('.eqdiv input').forEach(res => arr.push(Number(res.value)));
const num = arr.length;
const newarr = arr.reduce((a, b) => a + b);
const mean = newarr / num;
const resarr = [];
arr.forEach(re => resarr.push((re - mean) * (re - mean)));
const newresarr = resarr.reduce((c, v) => c + v);
const norDev = Math.sqrt(newresarr / (num - 1));
const dev = norDev / Math.sqrt(num);
let mpv;
if (!isNaN(dev)) {
mpv = `${mean.toFixed(3)} ± ${dev.toFixed(3)}`;
} else {
mpv = `${mean.toFixed(3)}`;
}
displayResult(mpv);
}
}
function unresFunc() {
if (checkIn('.wrap-sub') === false) {
errIn();
} else {
let allweight = [],
allValues = [],
subMeanArray = [],
sub = [],
semiFinal = [];
document.querySelectorAll('.div2-2 input').forEach(w => allweight.push(Number(w.value)));
const sumWeight = allweight.reduce((n, m) => n + m);
document.querySelectorAll('.div2-1 input').forEach(s => allValues.push(Number(s.value)));
for (i = 0; i < allweight.length; i++) {
subMeanArray.push(allweight[i] * allValues[i]);
}
const sumMean = subMeanArray.reduce((a, b) => a + b);
const mean = sumMean / sumWeight;
allValues.forEach(s => sub.push(s - mean));
for (i = 0; i < allweight.length; i++) {
semiFinal.push(allweight[i] * sub[i] * sub[i]);
}
const alFinal = semiFinal.reduce((a, b) => a + b);
const nDev = Math.sqrt(alFinal / allweight.length);
const Dev = nDev / Math.sqrt(sumWeight);
let mpv;
if (isNaN(Dev)) {
mpv = ` : No choice but the only value you gave which is ${allValues[0].toFixed(3)}`;
} else {
mpv = `${mean.toFixed(3)} ± ${Dev.toFixed(3)}`;
}
displayResult(mpv);
}
}
function displayResult(wow) {
document.querySelector('#output').innerHTML = `The Result is ${wow}`;
}
.wrap>div {
display: none;
}
/* .uneqdiv{
display: none;
} */
.container {
padding-top: 100px;
width: max-content;
padding-bottom: 50px;
}
.wrap {
margin-top: 50px;
}
.wrap-sub {
display: flex;
}
.div2-1 {
margin-right: 5px;
}
button {
outline: none;
transition: border .3s;
}
.active,
.active:focus {
border-color: #46d84d;
}
button.reset:hover,
button.un-reset:hover {
border-color: #f86868;
}
button.eqmore:hover,
button.un-eqmore:hover {
border-color: #46d84d;
}
.errdiv {
height: 40px;
width: 100%;
background-color: #df4f4f;
color: #ffffff;
display: flex !important;
justify-content: center;
align-items: center;
}
#output {
padding-top: 20px;
}
h6 {
margin: 0;
}
.tes {
display: inline-block;
}
#media(max-width: 700px) {
.container {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
}
.container>h2 {
font-size: 2rem;
}
button {
width: 75%;
max-width: 20rem;
}
.wrap {
display: flex;
flex-direction: column;
align-items: center;
width: 90%;
}
.eqdiv {
text-align: center;
}
.eqdiv input {
max-width: 20rem;
width: 75%;
}
.eqdiv button {
display: block;
margin-left: auto;
margin-right: auto;
}
.uneqdiv {
text-align: center;
width: 100%;
}
.wrap-sub input {
max-width: 25rem;
width: 100%;
}
.uneqdiv .wrap-sub {
width: 100%;
margin-top: 40px;
margin-bottom: 15px;
display: flex;
flex-direction: column;
align-items: center;
}
.wrap-sub h6 {
font-size: 1.4rem;
}
.tes button {
width: 100%;
}
}
<html>
<head>
<meta content="utf-8">
<meta name="viewport" content="initial-scale=1, width=device-width, viewport-fit=cover">
<link rel="stylesheet" type="text/css" href="css/normalize.css">
<link rel="stylesheet" type="text/css" href="css/skeleton.css">
<link rel="stylesheet" type="text/css" href="css/index.css">
<title>survey</title>
</head>
<body>
<div class="container">
<h2 class="jui">Please select the wanted method</h2>
<button class="eqbtn">equal weight</button>
<button class="uneqbtn">unequal weight</button>
<div class="wrap">
<div class="eqdiv">
<h4>Enter the equal values</h4>
<div>
<input type="number" class="init">
</div>
<button class="eqmore">add more +</button>
<button class="reset">reset</button>
<button class="result button-primary">result</button>
</div>
<div class="uneqdiv">
<h4>Enter the unequal values</h4>
<div class="wrap-sub">
<div class="div2-1">
<h6>The Measurements</h6>
<input type="number" class="un-init">
</div>
<div class="div2-2">
<h6>The Weights</h6>
<input type="number" class="un-weight">
</div>
</div>
<div class="tes">
<button class="un-eqmore">add more +</button>
<button class="un-reset">reset</button>
<button class="un-result button-primary">result</button>
</div>
</div>
</div>
<div id="output"></div>
</div>
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" src="js/index.js"></script>
<script type="text/javascript" src="js/app.js"></script>
</body>
</html>
Hello guys I hope you can help me with JavaScript, I'm trying to itarate over some divs, the issue is that when I iterate sometimes a div never change to the other divs, it suppose to be infinite, I will recive thousands of different divs with different height and it should create an other div container in the case it does not fits but I can not achieve it work's, I'm using Vanilla JavaScript because I'm lerning JavaScript Regards.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
.big_container{
height: 600px;
width: 150px;
background-color: #f1f1f1;
float: left;
}
.items{
background-color: gray;
height: 50px;
}
.new_container{
margin-bottom: 10px;
height: 300px;
width: 150px;
background-color: red;
float: left;
margin-left: 5px;
}
</style>
</head>
<body>
<div class="big_container">
<div class="items">1</div>
<div class="items">2</div>
<div class="items">3</div>
<div class="items">4</div>
<div class="items">5</div>
<div class="items">6</div>
<div class="items">7</div>
<div class="items">8</div>
<div class="items">9</div>
<div class="items">10</div>
<div class="items">11</div>
<div class="items">12</div>
<div class="items">13</div>
</div>
<div class="new_container">
</div>
</body>
<script>
number = 0
sum = 0
new_container = document.getElementsByClassName('new_container')[number].offsetHeight
divs = document.getElementsByClassName('items')
for ( var i = 0; i < divs.length; i++ ){
sum += this.document.getElementsByClassName( 'items' )[0].offsetHeight
if ( sum <= new_container ){
console.log(sum, "yes")
document.getElementsByClassName("new_container")[number].appendChild( this.document.getElementsByClassName( 'items' )[0] )
} else {
sum = 0
console.log(sum, "NO entra")
nuevo_contenedor = document.createElement('div'); // Creo un contenedor
nuevo_contenedor.className = "new_container";
nuevo_contenedor.setAttribute("style", "background-color: red;");
document.body.appendChild(nuevo_contenedor)
number += + 1
}
}
</script>
</html>
I really apreciate a hand.
I know that I'm late, but there is my approach how this can be done.
// generate items with different height
function generateItems(count) {
const arr = [];
for (let i = 0; i < count; i++) {
const div = document.createElement("DIV");
const height = Math.floor((Math.random() * 100) + 10);
div.setAttribute("style", `height: ${height}px`);
div.setAttribute("class", "items");
const t = document.createTextNode(i + 1);
div.appendChild(t);
arr.push(div);
}
return arr;
}
function createNewContainer(height) {
const new_container = document.createElement("DIV")
new_container.setAttribute("class", "new_container");
new_container.setAttribute("style", `height: ${height}px`)
document.body.appendChild(new_container);
return new_container;
}
function breakFrom(sourceContainerId, newContainerHeight) {
const srcContainer = document.getElementById(sourceContainerId);
const items = srcContainer.childNodes;
let new_container = createNewContainer(newContainerHeight);
let sumHeight = 0;
for (let i = 0; i < items.length; i++) {
let item = items[i];
if (item.offsetHeight > newContainerHeight) {
// stop!!! this item too big to fill into new container
throw new Error("Item too big.");
}
if (sumHeight + item.offsetHeight < newContainerHeight) {
// add item to new container
sumHeight += item.offsetHeight;
new_container.appendChild(item.cloneNode(true));
} else {
// create new container
new_container = createNewContainer(newContainerHeight);
new_container.appendChild(item.cloneNode(true));
// don't forget to set sumHeight)
sumHeight = item.offsetHeight;
}
}
// if you want to remove items from big_container
// for (let i = items.length - 1; i >= 0; i--) {
// srcContainer.removeChild(items[i]);
// }
}
// create big container with divs
const big_container = document.getElementById("big_container");
const items = generateItems(13);
items.forEach((div, index) => {
big_container.appendChild(div);
});
breakFrom("big_container", 300);
#big_container {
width: 150px;
background-color: #f1f1f1;
float: left;
}
.items {
background-color: gray;
border: 1px solid #000000;
text-align: center;
}
.new_container {
margin-bottom: 10px;
height: 300px;
width: 150px;
background-color: red;
border: 1px solid red;
float: left;
margin-left: 5px;
}
<div id="big_container"></div>
This example gives you the ability to play with divs of random height. Hope, this will help you.
I am building a timer that has a pause and resume function. I found out the user can set multiple intervals when the timer is clicked more than once.
How can I prevent users from setting multiple intervals?
I tried inserting ($('.clock)).off("click") once the interval has set, but then couldn't figure out how to resume it. I thought I could do a statement pause = true, but not sure how I can use it in my code.
let currentMins = 10
let currentCount = 10*60
let pause = true
$(document).ready(function(){
// --- SET TIME --- //
$('select').on('change', function(){
const timePair = {
pappardelle : 7,
penne : 10,
farfalle : 11,
bucatini : 8,
angelhair : 4,
gnocchi : 1,
orecchiette : 10,
justboiledeggs : 11
}
const selected = this.value
for(let keys in timePair){
let toPrint = ''
if(selected.split(' ').join('').toLowerCase() == keys){
toPrint = timePair[keys]
$('#mins').html(toPrint)
$('.clock').html(toPrint+':00')
currentMins = toPrint
currentCount = timePair[keys]*60
console.log('current counts on set: ',currentCount)
}
}
})
// --- UPDATE CLOCK --- //
//basic increment and decrement setting
$('.decrement').click(function(){
if((currentMins)>1){
currentMins-=1
currentCount-=60
$('#mins').html(currentMins)
$('.clock').html(currentMins + ':00')
console.log("current mins and count in decrement :", currentMins, currentCount)
}
})
$('.increment').click(function(){
if(currentMins<100){
currentMins+=1
currentCount += 60
$('#mins').html(currentMins)
$('.clock').html(currentMins + ':00')
console.log("current mins and count in increment :", currentMins, currentCount)
}
})
$('.clock').click(function(){
console.log("current currentCount in the starting clock div :", currentCount)
//interval setting
const interval = window.setInterval(function(){
if(currentCount == 0){
pause=true
$('.clock').html('Buon appetito!')
} else {
console.log("current currentCount in the else clause in clock div :", currentCount)
pause = false
currentCount --
let minuites = Math.floor(currentCount / 60)
let seconds = currentCount - minuites * 60
$('.clock').html(minuites + ':' + ('0' + seconds).slice(-2))
}
$('.pause').click(function(){
pause = true;
clearInterval(interval)
})
}, 1000)
$('select').on('change', function(){
pause = true;
clearInterval(interval)
})
})
})//end jquery
You can do that with a flag variable:
let started = false
and a conditional return statement:
if (started && !pause) {
return;
} else {
started = true;
}
All it does is when the clock is clicked, it checks to see if started is true. If it is, then the timer has already been enabled, so it just returns out of the function (unless it's paused). If the value of started is false, then the timer begins and the flag variable is set to true.
See this working example:
let currentMins = 10
let currentCount = 10 * 60
let pause = true
let started = false
$(document).ready(function() {
// --- SET TIME --- //
$('select').on('change', function() {
const timePair = {
pappardelle: 7,
penne: 10,
farfalle: 11,
bucatini: 8,
angelhair: 4,
gnocchi: 1,
orecchiette: 10,
justboiledeggs: 11
}
const selected = this.value
for (let keys in timePair) {
let toPrint = ''
if (selected.split(' ').join('').toLowerCase() == keys) {
toPrint = timePair[keys]
$('#mins').html(toPrint)
$('.clock').html(toPrint + ':00')
currentMins = toPrint
currentCount = timePair[keys] * 60
console.log('current counts on set: ', currentCount)
}
}
if (selected.indexOf('Seamless') != -1) {
window.open('http://seamless.com', '_blank')
}
})
// --- UPDATE CLOCK --- //
//basic increment and decrement setting
$('.decrement').click(function() {
if ((currentMins) > 1) {
currentMins -= 1
currentCount -= 60
$('#mins').html(currentMins)
$('.clock').html(currentMins + ':00')
console.log("current mins and count in decrement :", currentMins, currentCount)
}
})
$('.increment').click(function() {
if (currentMins < 100) {
currentMins += 1
currentCount += 60
$('#mins').html(currentMins)
$('.clock').html(currentMins + ':00')
console.log("current mins and count in increment :", currentMins, currentCount)
}
})
$('.clock').click(function() {
if (started && !pause) {
return;
} else {
started = true;
}
console.log("current currentCount in the starting clock div :", currentCount)
//interval setting
const interval = window.setInterval(function() {
if (currentCount == 0) {
pause = true
$('.clock').html('Buon appetito!')
} else {
console.log("current currentCount in the else clause in clock div :", currentCount)
pause = false
currentCount--
let minuites = Math.floor(currentCount / 60)
let seconds = currentCount - minuites * 60
$('.clock').html(minuites + ':' + ('0' + seconds).slice(-2))
}
$('.pause').click(function() {
pause = true;
clearInterval(interval)
})
}, 1000)
$('select').on('change', function() {
pause = true;
clearInterval(interval)
})
})
}) //end jquery
body {
margin: 50px;
font-family: 'Cormorant Garamond', serif;
color: tomato;
}
main {
justify-content: center;
}
h1 {
font-size: 40px;
text-align: center;
}
.grid {
display: grid;
grid-gap: 10px;
grid-template-columns: [col1-start] 130px [col2-start] 130px [col3-start] 140px [col3-end];
grid-template-rows: [row1-start] 120px [row2-start] 120px [row2-end];
background-color: #fff;
color: tomato;
justify-content: center;
}
.box {
color: tomato;
padding: 30px;
font-size: 150%;
border: 1px solid tomato;
}
.food {
grid-column: col1-start / col3-start;
grid-row: row1-start;
}
.clock {
grid-column: col3-start;
grid-row: row1-start / row2-end;
display: flex;
justify-content: center;
align-items: center;
}
.clock:hover {
color: #ffd700;
font-size: 25px;
cursor: pointer;
}
.settimer {
grid-column: col1-start;
grid-row: row2-start;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: center;
align-items: center;
align-content: stretch;
}
.settimer div {
margin: 5px;
}
#mins {
align-items: center;
font-size: 20px;
}
.icon {
font-size: 15px;
}
.icon:hover {
color: #ffd700;
cursor: pointer;
font-size: 18px;
}
.pause {
grid-column: col2-start;
grid-row: row2-start;
font-size: 20px;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
align-content: stretch;
}
.pause:hover {
color: #ffd700;
cursor: pointer;
}
<!DOCTYPE html>
<html>
<head>
<script defer src="https://use.fontawesome.com/releases/v5.0.6/js/all.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="pomodoro.css" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css?family=Cormorant+Garamond:400,700" rel="stylesheet">
<script src="pomodorooo.js"></script>
<title>Pomodoro Clock</title>
</head>
<body>
<main>
<h1>Pomodoro clock</h1>
<div class="grid">
<div class="box food">Set the timer for
<select id="pasta">
<option id="0">I meant, pasta</option>
<option id="1">Pappardelle</option>
<option id="2">Penne</option>
<option id="3">Farfalle</option>
<option id="4">Bucatini</option>
<option id="5">Angel Hair</option>
<option id="6">Gnocchi</option>
<option id="7">Orecchiette</option>
<option id="8">Just boiled eggs</option>
<option id="9">Take me to Seamless already</option>
</select>
<!-- deleted form -->
</div>
<!-- a click box that has various food options, default set for each food -->
<div class="box clock">Start</div>
<!-- a blank circle. will be filled red-->
<div class="box settimer">
<div class="decrement icon"><i class="fas fa-caret-left"></i></div>
<div id="mins">Ready</div>
<!-- deleted span -->
<div class="increment icon"><i class="fas fa-caret-right"></i></div>
</div>
<!-- timer set. increment and decrement enabled -->
<div class="box pause">Pause</div>
<!-- break set. increment and decrement enabled -->
</div>
</main>
<br /><br /><br /><br /><br /><br />
</body>
</html>