Reputation: 17
I copied some code from here.
The following code has some errors when the value reaches the minFontSize
or maxFontSize
.
I don't know how to fix this...
const decreaseFontSizeBtn = document.querySelector(".fontsize-via-btn #decrease");
const increaseFontSizeBtn = document.querySelector(".fontsize-via-btn #increase");
const fontSizeDisplay = document.querySelector(".fontsize-via-btn .current-fontsize");
const defaultFontSize = 20;
const minFontSize = 16;
const maxFontSize = 40;
let currentFontSize = defaultFontSize;
var timeout, interval;
[decreaseFontSizeBtn, increaseFontSizeBtn].forEach(btn => {
btn.addEventListener("mousedown", () => {
if (btn.id === "decrease") {
decreaseFontSize();
hold(decreaseFontSize);
}
if (btn.id === "increase") {
increaseFontSize();
hold(increaseFontSize);
}
saveFontSize();
})
btn.addEventListener("mouseup", clearTimers);
btn.addEventListener("mouseleave", clearTimers);
function clearTimers() {
clearTimeout(timeout);
clearInterval(interval);
}
})
function hold(func) {
timeout = setTimeout(() => {
interval = setInterval(() => {
func();
saveFontSize();
}, 50)
}, 300)
}
function decreaseFontSize() {
if (currentFontSize > minFontSize) {
currentFontSize -= 2;
}
if (currentFontSize === minFontSize) {
decreaseFontSizeBtn.disabled = true;
} else {
increaseFontSizeBtn.disabled = false;
}
}
function increaseFontSize() {
if (currentFontSize < maxFontSize) {
currentFontSize += 2;
}
if (currentFontSize === maxFontSize) {
increaseFontSizeBtn.disabled = true;
} else {
decreaseFontSizeBtn.disabled = false;
}
}
function saveFontSize() {
fontSizeDisplay.textContent = currentFontSize;
// localStorage ...
}
.fontsize-via-btn {
width: 100px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
font-size: 2rem;
}
<div class="fontsize-via-btn">
<button id="decrease">A-</button>
<div class="current-fontsize">20</div>
<button id="increase">A+</button>
</div>
Upvotes: 0
Views: 120
Reputation: 43990
Discrepancies
"mouseup" and "mouseleave" needs to be registered on each button or the events must be delegated to the buttons. Right now the mouse events only affect the <div>
which doesn't have any functions firing off on it. So that's why the buttons get stuck -- they are consantly repeating themselves even after the user releases the mousebutton.
The example below has separate event handlers for each button. Normally I prefer to use one event handler to control everything through
event delegation, but because of the need to use set\clearInterval()
as event handlers, the event object gets lost. I could solve that problem with a closure, but your code was close enough that a radical change wouldn't be to your benifet.
Saving with localStorage
(I know it's not part of the question) is omitted from this version since this site blocks it's use. I wrote another version that saves the current font-size and loads it as well, to review that one go to this Plunker.
const io = document.forms.UI.elements;
let current = 1.5;
let repeatInc = null;
let repeatDec = null;
function holdInc(e) {
if (repeatInc === null) {
repeatInc = setInterval(incFontSize, 100);
}
};
function releaseInc(e) {
if (repeatInc != null) {
clearInterval(repeatInc);
repeatInc = null;
}
};
function incFontSize() {
const io = document.forms.UI.elements;
const max = 4;
if (current < max) {
current += 0.25;
io.dec.disabled = false;
}
if (current == max) {
io.inc.disabled = true;
releaseInc();
}
io.fSize.value = current.toFixed(2);
io.text.style.fontSize = `${current}rem`
};
function holdDec(e) {
if (repeatDec == null) {
repeatDec = setInterval(decFontSize, 100);
}
};
function releaseDec(e) {
if (repeatDec != null) {
clearInterval(repeatDec);
repeatDec = null;
}
};
function decFontSize() {
const io = document.forms.UI.elements;
const min = 1;
if (current > min) {
current -= 0.25;
io.inc.disabled = false;
}
if (current == min) {
io.dec.disabled = true;
releaseDec()
}
io.fSize.value = current.toFixed(2);
io.text.style.fontSize = `${current}rem`
};
io.inc.addEventListener('mousedown', holdInc);
io.inc.addEventListener('mouseup', releaseInc);
io.inc.addEventListener('mousemove', releaseInc);
io.dec.addEventListener('mousedown', holdDec);
io.dec.addEventListener('mouseup', releaseDec);
io.dec.addEventListener('mouseleave', releaseDec);
:root {
font: 2ch/1 'Segoe UI'
}
body {
font-size: 1ch;
}
.ctrl {
display: flex;
align-items: center;
justify-content: flex-start;
font-size: 1rem;
padding: 5px;
}
#fSize {
display: block;
width: 5ch;
margin: 0 4px;
font-family: Consolas;
text-align: center;
}
button {
display: block;
width: max-content;
font: inherit;
cursor: pointer;
}
label {
display: block;
width: max-content;
margin: 0 8px;
font-size: 1rem;
}
.text {
min-height: 50px;
font-size: 1.25rem;
}
<form id='UI'>
<fieldset name='ctrl' class='ctrl'>
<button id="dec" type='button'>🗛-</button>
<output id="fSize">1.50</output>
<button id="inc" type='button'>🗚+</button>
<label>Scale: 1ch => 1rem</label>
</fieldset>
<fieldset name='text' class='text' contenteditable>
This test area has editable content
</fieldset>
</form>
Upvotes: 0
Reputation: 17400
Your timers are not cleared when you hit the minimum or maximum because (as @timmmmmb already said) disabled buttons don't trigger mouse events. Thus, when you try to go in the other direction, the original timer is again executed.
The simplest would probably be, calling clearTimers
also when you hit the end of the range
const decreaseFontSizeBtn = document.querySelector(".fontsize-via-btn #decrease");
const increaseFontSizeBtn = document.querySelector(".fontsize-via-btn #increase");
const fontSizeDisplay = document.querySelector(".fontsize-via-btn .current-fontsize");
const defaultFontSize = 20;
const minFontSize = 16;
const maxFontSize = 40;
let currentFontSize = defaultFontSize;
var timeout, interval;
function clearTimers() {
clearTimeout(timeout);
clearInterval(interval);
}
[decreaseFontSizeBtn, increaseFontSizeBtn].forEach(btn => {
btn.addEventListener("mousedown", () => {
if (btn.id === "decrease") {
decreaseFontSize();
hold(decreaseFontSize);
}
if (btn.id === "increase") {
increaseFontSize();
hold(increaseFontSize);
}
saveFontSize();
})
btn.addEventListener("mouseup", clearTimers);
btn.addEventListener("mouseleave", clearTimers);
})
function hold(func) {
timeout = setTimeout(() => {
interval = setInterval(() => {
func();
saveFontSize();
}, 50)
}, 300)
}
function decreaseFontSize() {
if (currentFontSize > minFontSize) {
currentFontSize -= 2;
}
if (currentFontSize === minFontSize) {
decreaseFontSizeBtn.disabled = true;
clearTimers();
} else {
increaseFontSizeBtn.disabled = false;
}
}
function increaseFontSize() {
if (currentFontSize < maxFontSize) {
currentFontSize += 2;
}
if (currentFontSize === maxFontSize) {
clearTimers();
increaseFontSizeBtn.disabled = true;
} else {
decreaseFontSizeBtn.disabled = false;
}
}
function saveFontSize() {
fontSizeDisplay.textContent = currentFontSize;
// localStorage ...
}
.fontsize-via-btn {
width: 100px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
font-size: 2rem;
}
<div class="fontsize-via-btn">
<button id="decrease">A-</button>
<div class="current-fontsize">20</div>
<button id="increase">A+</button>
</div>
Upvotes: 2
Reputation: 786
I think the problem is, that when you reach the max Fontsize you disable the button and that means your mouseup event will not be triggerd. I moved the mouseup event away from the buttons and on the container and i think it works now.
const decreaseFontSizeBtn = document.querySelector(".fontsize-via-btn #decrease");
const increaseFontSizeBtn = document.querySelector(".fontsize-via-btn #increase");
const fontSizeDisplay = document.querySelector(".fontsize-via-btn .current-fontsize");
const fontSizeContainer = document.querySelector(".fontsize-via-btn");
const defaultFontSize = 20;
const minFontSize = 16;
const maxFontSize = 40;
let currentFontSize = defaultFontSize;
var timeout, interval;
fontSizeContainer.addEventListener("mouseup", clearTimers);
fontSizeContainer.addEventListener("mouseleave", clearTimers);
function clearTimers() {
clearTimeout(timeout);
clearInterval(interval);
}
[decreaseFontSizeBtn, increaseFontSizeBtn].forEach(btn => {
btn.addEventListener("mousedown", () => {
if (btn.id === "decrease") {
decreaseFontSize();
hold(decreaseFontSize);
}
if (btn.id === "increase") {
increaseFontSize();
hold(increaseFontSize);
}
saveFontSize();
})
})
function hold(func) {
timeout = setTimeout(() => {
interval = setInterval(() => {
func();
saveFontSize();
}, 50)
}, 300)
}
function decreaseFontSize() {
if (currentFontSize > minFontSize) {
currentFontSize -= 2;
}
if (currentFontSize === minFontSize) {
decreaseFontSizeBtn.disabled = true;
} else {
increaseFontSizeBtn.disabled = false;
}
}
function increaseFontSize() {
if (currentFontSize < maxFontSize) {
currentFontSize += 2;
}
if (currentFontSize === maxFontSize) {
increaseFontSizeBtn.disabled = true;
} else {
decreaseFontSizeBtn.disabled = false;
}
}
function saveFontSize() {
fontSizeDisplay.textContent = currentFontSize;
// localStorage ...
}
.fontsize-via-btn {
width: 100px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
font-size: 2rem;
}
<div class="fontsize-via-btn">
<button id="decrease">A-</button>
<div class="current-fontsize">20</div>
<button id="increase">A+</button>
</div>
Upvotes: 0