I've built the following function, which changes the content of a span on mouseover. Everything works fine. The only problem is that I'm not sure how to stop the function on mouseout (initial and mouseout state should be the same).
Here is my current solution.
var squWrd = document.getElementById("squWrd");
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
squWrd.onmouseover = function () {
squWrd.innerHTML = "Design.";
sleep(250).then(() => { squWrd.innerHTML = "UX."; });
sleep(500).then(() => { squWrd.innerHTML = "Marketing."; });
sleep(750).then(() => { squWrd.innerHTML = "Social Media."; });
sleep(1000).then(() => { squWrd.innerHTML = "Education."; });
sleep(1250).then(() => { squWrd.innerHTML = "Branding."; });
sleep(1500).then(() => { squWrd.innerHTML = "Packaging."; });
sleep(1750).then(() => { squWrd.innerHTML = "Design."; });
sleep(2000).then(() => { squWrd.innerHTML = "Processes."; });
sleep(2250).then(() => { squWrd.innerHTML = "E-Commerce."; });
sleep(2500).then(() => { squWrd.innerHTML = "Advertising."; });
sleep(2750).then(() => { squWrd.innerHTML = "Photos."; });
sleep(3000).then(() => { squWrd.innerHTML = "Products."; });
sleep(3250).then(() => { squWrd.innerHTML = "Logos."; });
sleep(3500).then(() => { squWrd.innerHTML = "Emotions."; });
sleep(3750).then(() => { squWrd.innerHTML = "Solutions."; });
}
squWrd.onmouseout = function () {
squWrd.innerHTML = "Solutions.";
}
Do you guys have any suggestions? Thanks in advance!
Timeouts are still running, you need to call clearTimeout. I recommend you to add a second argument to the sleep function, a callback function that pass the timeout reference, this way you can clear only the timeouts related to the texts and not all the timers.
Also instead of call sleep for every text, you can store this texts inside an array an iterate over it:
var squWrd = document.getElementById("squWrd");
function sleep(ms, cb=()=> {}) {
return new Promise(resolve => {
const time = setTimeout(() => {
resolve();
}, ms);
cb(time);
});
}
const texts = ["Design", "UX.", "Marketing.", "Social Media.", "Education.", "Branding.", "Packaging.", "Design.", "Processes.", "E-Commerce.", "Advertising.", "Photos.", "Products.", "Logos.", "Emotions.", "Solutions."];
const textTimeouts = [];
squWrd.onmouseover = function() {
texts.forEach((text, i) => {
sleep(250 * i, (time) => textTimeouts.push(time)).then(res => {
squWrd.innerHTML = text;
});
});
};
squWrd.onmouseout = function() {
squWrd.innerHTML = "Solutions.";
textTimeouts.forEach(time => clearTimeout(time));
};
<h1 id="squWrd">Solutions</h1>
The problem is that even when onmouseout gets triggered, there are still sleep promises pending. You need to save the reference for each setTimeout call and clear it at onmouseout event. See here.
var squWrd = document.getElementById('squWrd');
var timeoutRefs = [];
function sleep(ms) {
return new Promise(resolve => timeoutRefs.push(setTimeout(resolve, ms)));
}
squWrd.onmouseover = function () {
squWrd.innerHTML = "Design.";
sleep(250).then(() => { squWrd.innerHTML = "UX."; });
sleep(500).then(() => { squWrd.innerHTML = "Marketing."; });
sleep(750).then(() => { squWrd.innerHTML = "Social Media."; });
sleep(1000).then(() => { squWrd.innerHTML = "Education."; });
sleep(1250).then(() => { squWrd.innerHTML = "Branding."; });
sleep(1500).then(() => { squWrd.innerHTML = "Packaging."; });
sleep(1750).then(() => { squWrd.innerHTML = "Design."; });
sleep(2000).then(() => { squWrd.innerHTML = "Processes."; });
sleep(2250).then(() => { squWrd.innerHTML = "E-Commerce."; });
sleep(2500).then(() => { squWrd.innerHTML = "Advertising."; });
sleep(2750).then(() => { squWrd.innerHTML = "Photos."; });
sleep(3000).then(() => { squWrd.innerHTML = "Products."; });
sleep(3250).then(() => { squWrd.innerHTML = "Logos."; });
sleep(3500).then(() => { squWrd.innerHTML = "Emotions."; });
sleep(3750).then(() => { squWrd.innerHTML = "Solutions."; });
};
squWrd.onmouseout = function () {
timeoutRefs.forEach(function (timeoutRef) {
clearTimeout(timeoutRef)
});
timeoutRefs = [];
squWrd.innerHTML = 'Solutions.';
};
<div id="squWrd">INITIAL VALUE</div>
Related
this is my code snippet bellow,
I am trying to close the websocket connection after component unmounts, I just totally dont know how to do it
I am using this useEffect inside the same component I am also using useref to count the mounted count of the component so that the websocket doesn't creates more that 1 instance at a time
const mountedCount = useRef(0);
useEffect(() => {
const handleWebsocket = () => {
mountedCount.current++;
const socketURL = 'socket url here'
const socket = new WebSocket(socketURL);
socket.onopen = () => {
console.log('socket open')
};
socket.onclose = (closeEvent) => {
if (closeEvent.wasClean) return;
timeout = setTimeout(() => {
handleWebsocket();
}, envVariables.webSocketReconnectionTimeout);
};
socket.onerror = () => {
console.log('error here')
};
socket.onmessage = (messageEvent) => {
console.log('got the message')
};
return socket;
};
if (mountedCount.current === 0) {
handleWebsocket();
}
return () => {
clearTimeout(timeout);
};
}, [
dispatch,
userData.userInformation,
wss.connectionStatus
]);
const mountedCount = useRef(0);
const [currentSocket,setCurrentSocket]=useState(null)
useEffect(() => {
const handleWebsocket = () => {
mountedCount.current++;
const socketURL = 'socket url here'
const socket = new WebSocket(socketURL);
socket.onopen = () => {
console.log('socket open')
};
socket.onclose = (closeEvent) => {
if (closeEvent.wasClean) return;
timeout = setTimeout(() => {
handleWebsocket();
}, envVariables.webSocketReconnectionTimeout);
};
socket.onerror = () => {
console.log('error here')
};
socket.onmessage = (messageEvent) => {
console.log('got the message')
};
return socket;
};
if (mountedCount.current === 0) {
setCurrentSocket(handleWebsocket());
}
return () => {
clearTimeout(timeout);
currentSocket?.close();
};
}, [
dispatch,
userData.userInformation,
wss.connectionStatus
]);
or you can declare socket variable in one upper scope:
const mountedCount = useRef(0);
useEffect(() => {
let socket
const handleWebsocket = () => {
mountedCount.current++;
const socketURL = 'socket url here'
socket = new WebSocket(socketURL);
socket.onopen = () => {
console.log('socket open')
};
socket.onclose = (closeEvent) => {
if (closeEvent.wasClean) return;
timeout = setTimeout(() => {
handleWebsocket();
}, envVariables.webSocketReconnectionTimeout);
};
socket.onerror = () => {
console.log('error here')
};
socket.onmessage = (messageEvent) => {
console.log('got the message')
};
return socket;
};
if (mountedCount.current === 0) {
handleWebsocket();
}
return () => {
clearTimeout(timeout);
socket.close()
};
}, [
dispatch,
userData.userInformation,
wss.connectionStatus
]);
I am trying to make button stay disabled when input not valid...
I am using just simple Javascript validation. I can't make it work. Help please?
const hasInvalidInput = (inputList) => {
return inputList.some((inputElement) => {
return !inputElement.validity.valid;
});
};
const toggleButtonState = (inputList, buttonElement, inactiveButtonClass) => {
if (hasInvalidInput(inputList)) {
buttonElement.classList.add(inactiveButtonClass);
buttonElement.setAttribute("disabled", "");
} else {
buttonElement.classList.remove(inactiveButtonClass);
buttonElement.removeAttribute("disabled", "");
}
};
const checkInputValidity = (formElement, inputElement, inputErrorClass, errorClass) => {
if (!inputElement.validity.valid) {
showInputError(formElement, inputElement, inputElement.validationMessage, inputErrorClass, errorClass);
} else {
hideInputError(formElement, inputElement, inputErrorClass, errorClass);
}
};
const showInputError = (formElement, inputElement, errorMessage) => {
const errorElement = formElement.querySelector(.${inputElement.id}-error);
inputElement.classList.add("popup__error");
errorElement.textContent = errorMessage;
errorElement.classList.add("popup__error_visible");
};
const hideInputError = (formElement, inputElement, inputErrorClass, errorClass) => {
const errorElement = formElement.querySelector(`.${inputElement.id}-error`);
inputElement.classList.remove(inputErrorClass);
errorElement.classList.remove(errorClass);
errorElement.textContent = "";
};
const setEventListeners = (formElement, inputSelector, submitButtonSelector, inactiveButtonClass, inputErrorClass, errorClass) => {
const inputList = Array.from(formElement.querySelectorAll(inputSelector));
const buttonElement = formElement.querySelector(submitButtonSelector);
inputList.forEach((inputElement) => {
inputElement.addEventListener("input", () => {
checkInputValidity(formElement, inputElement, inputErrorClass, errorClass);
toggleButtonState(inputList, buttonElement, inactiveButtonClass);
});
});
};
const enableValidation = ({
formSelector,
inputSelector,
submitButtonSelector,
inactiveButtonClass,
inputErrorClass,
errorClass,
}) => {
const formList = Array.from(document.querySelectorAll(formSelector));
formList.forEach((formElement) => {
formElement.addEventListener("submit", function (evt) {
evt.preventDefault();
});
setEventListeners(
formElement,
inputSelector,
submitButtonSelector,
inactiveButtonClass,
inputErrorClass,
errorClass
);
});
};
enableValidation({
formSelector: ".popup__form",
inputSelector: ".popup__input",
submitButtonSelector: ".popup__button",
inactiveButtonClass: "popup__button_disabled",
inputErrorClass: "popup__error",
errorClass: "popup__error_visible"
});
Is there a polyfill for window.showOpenFilePicker (MDN)?
function showOpenFilePickerPolyfill(options) {
return new Promise((resolve) => {
const input = document.createElement("input");
input.type = "file";
input.multiple = options.multiple;
input.accept = options.types
.map((type) => type.accept)
.flatMap((inst) => Object.keys(inst).flatMap((key) => inst[key]))
.join(",");
input.addEventListener("change", () => {
resolve(
[...input.files].map((file) => {
return {
getFile: async () =>
new Promise((resolve) => {
resolve(file);
}),
};
})
);
});
input.click();
});
}
if (typeof window.showOpenFilePicker !== 'function') {
window.showOpenFilePicker = showOpenFilePickerPolyfill
}
EDIT
i was able to get it to work, but one problem now is, before it creates and additional empty item before showing other items.
NB the load on demand function works fine, but i don't know why an additional empty item is created. i guess there's an issue with my code
const viewModel = observableModule.fromObject({
_sourceDataItems: [],
dataItems: new ObservableArray(),
initDataItems: function () {
var url="https://adekunletestprojects.000webhostapp.com/skog/searchResults.php?search=" + encodeURIComponent("Adeyeye") + "&location=" + encodeURIComponent("Lagos");
fetch(url).then((response) => response.json()).then((res) => {
this._sourceDataItems = new ObservableArray(res.items);
this.dataItems.push(this._sourceDataItems);
}).catch((err) => {
var toast = Toast.makeText("Unable to load users");
toast.show();
});
},
addMoreItemsFromSource: function (chunkSize) {
console.log(this._sourceDataItems);
let newItems = this._sourceDataItems.splice(0, chunkSize);
this.dataItems.push(newItems);
},
onLoadMoreItemsRequested: function (args) {
console.log("---load more item---");
const that = new WeakRef(this);
const listView = args.object;
if (this._sourceDataItems.length > 0) {
setTimeout(function () {
that.get().addMoreItemsFromSource(10);
listView.notifyLoadOnDemandFinished();
}, 1500);
args.returnValue = true;
} else {
args.returnValue = false;
listView.notifyLoadOnDemandFinished(true);
}
},
});
Search-view-model.js
_sourceDataItems: new ObservableArray(),
dataItems: new ObservableArray(),
initDataItems: function () {
var url = "https://adekunletestprojects.000webhostapp.com/skog/searchResults.php?search=" + encodeURIComponent("Adeyeye") + "&location=" + encodeURIComponent("Lagos");
fetch(url).then((response) => response.json()).then((res) => {
this._sourceDataItems = res.items;
this.addMoreItemsFromSource(6);
}).catch((err) => {
alert(err.message);
});
},
addMoreItemsFromSource: function (chunkSize) {
console.log(this._sourceDataItems);
let newItems = this._sourceDataItems.splice(0, chunkSize);
this.dataItems.push(newItems);
},
onLoadMoreItemsRequested: function (args) {
console.log("---load more item---");
const that = new WeakRef(this);
const listView = args.object;
if (this._sourceDataItems.length > 0) {
setTimeout(function () {
that.get().addMoreItemsFromSource(10);
listView.notifyLoadOnDemandFinished();
}, 1500);
args.returnValue = true;
} else {
args.returnValue = false;
listView.notifyLoadOnDemandFinished(true);
}
},
Search.js
exports.pageLoaded = function (args) {
const page = args.object;
var searchViewModel = new SearchViewModel();
page.bindingContext = searchViewModel;
searchViewModel.initDataItems();
searchViewModel.addMoreItemsFromSource(5);
}
I have two .js-files:
main.js
require("./randomEvent.js").start("hey");
require("./randomEvent.js").start("hi");
require("./randomEvent.js").start("hello");
randomEvent.js
var repeat = true;
exports.start = (randomString) => {
while (repeat) {
console.log(randomString);
}
}
exports.stop = (randomString) => {
repeat = false;
}
I want to start randomEvent.js 3 times, each with different randomStrings.
And if I do
require("./randomEvent.js").stop("hi");
it should stop the start("hi") function / it should set repeat to false.
How?
Thanks in advance!
You can implement your randomEvents.js as a class. So every instance has its own repeat flag.
function RandomEvent(str) {
this.repeat = true;
this.randomString = str;
this.start = () => {
setInterval(() => {
if (this.repeat) {
console.log('-->', this.randomString);
}
}, 1000);
}
this.stop = () => {
this.repeat = false;
}
}
module.exports = RandomEvent;
and main.js
let RandomEvent = require('./randomEvent');
let sayHi = new RandomEvent('hi');
let sayHello = new RandomEvent('hello');
let sayHey = new RandomEvent('hey');
sayHi.start();
sayHello.start();
sayHey.start();
setTimeout(() => {
console.log('stop saying hi')
sayHi.stop();
}, 5000);
Or you can store for every string, its own flag:
randomEvents.js
var repeat = {};
exports.start = (randomString) => {
repeat[randomString] = true;
setInterval(() => {
if (repeat[randomString]) {
console.log('-->', randomString);
}
}, 1000);
}
exports.stop = (randomString) => {
repeat[randomString] = false;
}
and in main.js
require("./randomEvent.js").start("hey");
require("./randomEvent.js").start("hi");
require("./randomEvent.js").start("hello");
setTimeout(() => {
console.log('stop saying hi')
require("./randomEvent.js").stop("hi");
}, 5000);
randomEvent.js
var repeatFlags = {};
function repeatLog(str) {
if (repeatFlags[str]) {
console.log(str);
setTimeout(() => {
repeatLog(str);
});
}
}
exports.start = (randomString) => {
repeatFlags[randomString] = true;
repeatLog(randomString);
}
exports.stop = (randomString) => {
repeatFlags[randomString] = false;
}