I have an anchor tag that plays a sound everytime the link is hovered:
<a onmouseenter="playAudio();">LINK</a>
However, when hovered, the link creates some characters that, if I keep moving the mouse inside the element, it keeps playing the sound over and over again.
Is there a way to play the onmouseenter function only once until it has detected the event onmouseout?
I've tried adding:
<a onmouseenter="playAudio();this.onmouseenter = null;">LINK</a>
but then it only plays the sound once.
Here goes the TextScramble animation function:
// ——————————————————————————————————————————————————
// TextScramble
// ——————————————————————————————————————————————————
class TextScramble {
constructor(el) {
this.el = el
this.chars = '!<>-_\\/[]{}—=+*^?#________'
this.update = this.update.bind(this)
}
setText(newText) {
const oldText = this.el.innerText
const length = Math.max(oldText.length, newText.length)
const promise = new Promise((resolve) => this.resolve = resolve)
this.queue = []
for (let i = 0; i < length; i++) {
const from = oldText[i] || ''
const to = newText[i] || ''
const start = Math.floor(Math.random() * 40)
const end = start + Math.floor(Math.random() * 40)
this.queue.push({
from,
to,
start,
end
})
}
cancelAnimationFrame(this.frameRequest)
this.frame = 0
this.update()
return promise
}
update() {
let output = ''
let complete = 0
for (let i = 0, n = this.queue.length; i < n; i++) {
let {
from,
to,
start,
end,
char
} = this.queue[i]
if (this.frame >= end) {
complete++
output += to
} else if (this.frame >= start) {
if (!char || Math.random() < 0.28) {
char = this.randomChar()
this.queue[i].char = char
}
output += `<span class="dud">${char}</span>`
} else {
output += from
}
}
this.el.innerHTML = output
if (complete === this.queue.length) {
this.resolve()
} else {
this.frameRequest = requestAnimationFrame(this.update)
this.frame++
}
}
randomChar() {
return this.chars[Math.floor(Math.random() * this.chars.length)]
}
}
// ——————————————————————————————————————————————————
// Configuration
// ——————————————————————————————————————————————————
const phrases = [
'MAIN_HUB'
]
const el = document.querySelector('.link_mainhub')
const fx = new TextScramble(el)
let counter = 0
const next = () => {
fx.setText(phrases[counter]).then(() => {});
}
el.addEventListener('mouseenter', next);
<a class="link_mainhub" onmouseenter="playAudio();">LINK</a>
Thanks.
Set a variable when you start playing the audio, and check for this before playing it again. Have the animation unset the variable when it's done.
function audioPlaying = false;
function playAudio() {
if (!audioPlaying) {
audioPlaying = true;
audioEl.play();
}
}
const next = () => {
fx.setText(phrases[counter]).then(() => {
audioPlaying = false;
});
}
Related
I am building a game, where the computer makes a patter of four colors and you have to remember the pattern and repeat it.
And it works almost fine, but there is one error which i cant figure out.
When i played a round and lost and the game restarts the level value goes to 2 instead of 1.
And when the new game starts the first two buttons get pressed at the same time.
At the first round everything works fine but the next round not.
var gamePattern = [];
var playerPattern = [];
var buttonColors = ["red", "blue", "green", "yellow"]
var level = 0;
$(".btn").click(function () {
if (patternDone) {
pressButton(this.id);
playerPattern.push(this.id);
checkAnswer();
}
});
function resetGame() {
gamePattern = [];
playerPattern = [];
gamePattern.length = 0;
playerPattern.length = 0;
patternDone = false;
level = 0;
$("h1").html("Press A Key to Start")
$(document).keypress(startGame);
}
//start
$(document).keypress(startGame);
//start Game
function startGame() {
level = level + 1;
console.log("level " + level);
console.log(level);
gamePattern = [];
playerPattern = [];
createLevel();
console.log(gamePattern)
playPattern();
patternDone = true;
$("h1").html("Level " + level)
}
//play the patter
async function playPattern() {
for (k = -1; k < level; k++) {
d = level - k;
// console.log(gamePattern);
abcColor = gamePattern[gamePattern.length - d];
console.log(abcColor);
await delay(1000);
pressButton(abcColor);
d = 0;
}
}
//create the level
function createLevel() {
for (y = 0; y <= level; y++) {
var randomColor = buttonColors[Math.floor(Math.random() * buttonColors.length)];
gamePattern.push(randomColor);
}
}
//update h1
function h1Level() {
levelCopy = level + 1;
$("h1").html("level " + levelCopy);
}
//pressButton
function pressButton(colord) {
// console.log(colord);
animatePress(colord);
playSound(nameSound = colord);
}
// Sound
function playSound(nameSound) {
var audio = new Audio("sounds/" + nameSound + ".mp3");
audio.play();
}
// animateClick
function animatePress(currentColor) {
$("#" + currentColor).addClass("pressed");
// console.log(currentColor);
setTimeout(function () {
$("#" + currentColor).removeClass("pressed");
}, 100);
}
//delay
const delay = millis => new Promise((resolve, reject) => {
setTimeout(_ => resolve(), millis)
});
//Button click and deciding if its right
async function checkAnswer() {
if (playerPattern.length === gamePattern.length) {
if (playerPattern.join(',') === gamePattern.join(',')) {
$("h1").html("Correct!");
console.log("correct");
await delay(1000);
startGame();
} else if (playerPattern.join(',') !== gamePattern.join(',')) {
$("h1").html("Wrong!");
level = 0;
console.log("wrong");
await delay(3000);
resetGame();
}
}
}
I have a small problem that could ruin the game. Once you click a card you need to search for the other card with the same picture right? but the problem is you can also double click and it will think you found the other card. Anyone know how o solf this problem?
const cards = document.querySelectorAll(".cards .card");
cards.forEach((card) => {
card.addEventListener("click", () => {
card.classList.add("clicked");
if (counter === 0) {
firstSelection = card.getAttribute("meme");
counter++;
} else {
secondSelection = card.getAttribute("meme");
counter = 0;
if (firstSelection === secondSelection) {
const correctCards = document.querySelectorAll(".card[meme='" + firstSelection + "']");
score.innerHTML = parseInt(score.innerHTML) + 1
if (score.innerHTML >= 8)
correctCards[0].classList.add("checked");
correctCards[0].classList.remove("clicked");
correctCards[1].classList.add("checked");
correctCards[1].classList.remove("clicked");
} else {
const incorrectCards = document.querySelectorAll(".card.clicked");
incorrectCards[0].classList.add("red");
incorrectCards[1].classList.add("red");
setTimeout(() => {
incorrectCards[0].classList.remove("red");
incorrectCards[0].classList.remove("clicked");
incorrectCards[1].classList.remove("red");
incorrectCards[1].classList.remove("clicked");
}, 600);
}
}
});
});
You can set 2 global vars firstClickedCard and secondClickedCard, and on click, assign the card element (event.currentTarget) to these variables. Then on second click, check if (secondClickedCard === firstClickedCard) before any of your other logic, and reject the click event if this is true.
This should verify if the actual clicked element (not just the meme attribute) are the same or not.
Note: this is untested, and would be easier to verify if your question included a minimal, reproducible example.
const cards = document.querySelectorAll(".cards .card");
var firstClickedCard;
var secondClickedCard;
cards.forEach((card) => {
card.addEventListener("click", (e) => {
card.classList.add("clicked");
if (counter === 0) {
firstClickedCard = e.currentTarget;
firstSelection = card.getAttribute("meme");
counter++;
} else {
secondClickedCard = e.currentTarget;
// reject this click
if (secondClickedCard === firstClickedCard) {
secondClickedCard = null;
return false;
}
secondSelection = card.getAttribute("meme");
counter = 0;
if (firstSelection === secondSelection) {
const correctCards = document.querySelectorAll(".card[meme='" + firstSelection + "']");
score.innerHTML = parseInt(score.innerHTML) + 1
if (score.innerHTML >= 8)
correctCards[0].classList.add("checked");
correctCards[0].classList.remove("clicked");
correctCards[1].classList.add("checked");
correctCards[1].classList.remove("clicked");
} else {
const incorrectCards = document.querySelectorAll(".card.clicked");
incorrectCards[0].classList.add("red");
incorrectCards[1].classList.add("red");
setTimeout(() => {
incorrectCards[0].classList.remove("red");
incorrectCards[0].classList.remove("clicked");
incorrectCards[1].classList.remove("red");
incorrectCards[1].classList.remove("clicked");
}, 600);
}
}
});
});
Although there is a double click event (dblclick), it is not supported by all browsers. The way around this is to test for double clicks within your click event handlers and use a timer function (to see if two clicks happened within a select amount of time). I use 300ms for the timer, but you may have to play around with that a bit.
const cards = document.querySelectorAll(".cards .card");
let clickCount = 0;
cards.forEach((card) => {
card.addEventListener("click", () => {
clickCount ++;
if (clickCount == 1) {
clickTimer = setTimeout(() => {
clickCount = 0;
//single click functionality here
card.classList.add("clicked");
if (counter === 0) {
firstSelection = card.getAttribute("meme");
counter++;
} else {
secondSelection = card.getAttribute("meme");
counter = 0;
if (firstSelection === secondSelection) {
const correctCards = document.querySelectorAll(".card[meme='" + firstSelection + "']");
score.innerHTML = parseInt(score.innerHTML) + 1
if (score.innerHTML >= 8)
correctCards[0].classList.add("checked");
correctCards[0].classList.remove("clicked");
correctCards[1].classList.add("checked");
correctCards[1].classList.remove("clicked");
} else {
const incorrectCards = document.querySelectorAll(".card.clicked");
incorrectCards[0].classList.add("red");
incorrectCards[1].classList.add("red");
setTimeout(() => {
incorrectCards[0].classList.remove("red");
incorrectCards[0].classList.remove("clicked");
incorrectCards[1].classList.remove("red");
incorrectCards[1].classList.remove("clicked");
}, 600);
}
}
}, 300);
} else if (clickCount == 2) {
clearTimeout(clickTimer);
clickCount = 0;
console.log("This was a double click!");
}
});
});
I have this slider done but not automatically sliding, what code should I add to make this automatic sliding? And where should I place that code in this?
I've tried to put setinterval for the goNext function but still not working
class Slider {
constructor(props) {
this.rootElement = props.element;
this.slides = Array.from(
this.rootElement.querySelectorAll(".slider-list__item")
);
this.slidesLength = this.slides.length;
this.current = 0;
this.isAnimating = false;
this.direction = 1; // -1
this.navBar = this.rootElement.querySelector(".slider__nav-bar");
this.thumbs = Array.from(this.rootElement.querySelectorAll(".nav-control"));
this.prevButton = this.rootElement.querySelector(".slider__arrow_prev");
this.nextButton = this.rootElement.querySelector(".slider__arrow_next");
this.slides[this.current].classList.add("slider-list__item_active");
this.thumbs[this.current].classList.add("nav-control_active");
this._bindEvents();
}
goTo(index, dir) {
if (this.isAnimating) return;
var self = this;
let prevSlide = this.slides[this.current];
let nextSlide = this.slides[index];
self.isAnimating = true;
self.current = index;
nextSlide.classList.add("slider-list__item_active");
goStep(dir) {
let index = this.current + dir;
let len = this.slidesLength;
let currentIndex = (index + len) % len;
this.goTo(currentIndex, dir);
}
goNext() {
this.goStep(1);
}
goPrev() {
this.goStep(-1);
}
_navClickHandler(e) {
var self = this;
if (self.isAnimating) return;
let target = e.target.closest(".nav-control");
if (!target) return;
let index = self.thumbs.indexOf(target);
if (index === self.current) return;
let direction = index > self.current ? 1 : -1;
self.goTo(index, direction);
}
_bindEvents() {
var self = this;
["goNext", "goPrev", "_navClickHandler"].forEach(method => {
self[method] = self[method].bind(self);
});
self.nextButton.addEventListener("click", self.goNext);
self.prevButton.addEventListener("click", self.goPrev);
self.navBar.addEventListener("click", self._navClickHandler);
}
}
// ===== init ======
let slider = new Slider({
element: document.querySelector(".slider")
});
So please if anyone could tell me how to make this carousel slider automatically sliding, I will really really appreciate that.
Just call the next function
setInterval(function()
{
slider.goNext();
}, 1000);
So i have a class that has some functions that are making a carousel work. But pressing the next button fast deletes the previous animation and starts the next so i want to make the button wait for the animation to end. The animation inside css is 0.6 seconds but the timeous i set inside goNext functions is completely ignored when i fast click the button. What am I doing wrong here?
index.js
let carousel = document.getElementById("carousel");
let seats = document.querySelectorAll("ul > li");
if (seats.length === 1)
carousel.style.left = 0;
class SLID {
constructor() {
this.nextDisable = true;
this.changeNextDisable = this.changeNextDisable.bind(this);
}
changeNextDisable() {
this.nextDisable = false;
}
goToNext() {
if(this.nextDisable===false){
this.nextDisable = true;
var el, i, j, new_seat, ref;
el = document.querySelector("ul > li.is-ref");
el.classList.remove('is-ref');
new_seat = el.nextElementSibling || seats[0];
new_seat.classList.add('is-ref');
new_seat.style.order = 1;
for (i = j = 2, ref = seats.length; (2 <= ref ? j <= ref : j >= ref); i = 2 <= ref ? ++j : --j) {
new_seat = new_seat.nextElementSibling || seats[0];
new_seat.style.order = i;
}
carousel.classList.remove('toPrev');
carousel.classList.add('toNext');
carousel.classList.remove('is-set');
return setTimeout(()=> {
this.changeNextDisable();
carousel.classList.add('is-set');
}, 60);
}
goToPrev() {
var el, i, j, new_seat, ref;
el = document.querySelector("ul > li.is-ref");
el.classList.remove('is-ref');
new_seat = el.previousElementSibling || seats[seats.length - 1];
new_seat.classList.add('is-ref');
new_seat.style.order = 1;
for (i = j = 2, ref = seats.length; (2 <= ref ? j <= ref : j >= ref); i = 2 <= ref ? ++j : --j) {
new_seat = new_seat.nextElementSibling || seats[0];
new_seat.style.order = i;
}
carousel.classList.remove('toNext');
carousel.classList.add('toPrev');
carousel.classList.remove('is-set');
return setTimeout((function () {
return carousel.classList.add('is-set');
}), 50);
}
}
}
let s = new SLID();
document.getElementById("nextButton").addEventListener("click", () => {
s.goToNext();
});
document.getElementById("prevButton").addEventListener("click", () => {
s.goToPrev();
});
According to your animation type (transition or animation) you can replace
return setTimeout(()=> {
this.changeNextDisable();
carousel.classList.add('is-set');
}, 60);
(and trying avoid it) to:
carousel.addEventListener("animationend", () => {
this.changeNextDisable();
carousel.classList.add('is-set');
}, {
once: true,
});
or
carousel.addEventListener("transitionend", () => {
this.changeNextDisable();
carousel.classList.add('is-set');
}, {
once: true,
});
same thing for goToPrev method.
You tell us that the CSS animation has a duration of 0.6 seconds, which is 600 milliseconds, not 60 or 50 milliseconds, which is what you set your setTimeout delay to.
So:
return setTimeout(()=> {
this.changeNextDisable();
carousel.classList.add('is-set');
}, 600);
// ^^^^
Having said that, you should look into listening to the transactionend event, as then you don't need to know how long the CSS transition takes.
That would look like this:
const done = () => {
carousel.removeEventListener("transactionend", done);
this.changeNextDisable();
carousel.classList.add('is-set');
}
return carousel.addEventListener("transactionend", done);
The bot replies well when a command is sent.
How do I make the WhatsApp web bot to reply with an image pulled from a URL? I want it to be able to reply with an image pulled from a URL, for example, www.school.com/pic.jpg. On the code if a user text #time it replies with time and Date but I want it to reply with an image.
//
// FUNCTIONS
//
// Get random value between a range
function rand(high, low = 0) {
return Math.floor(Math.random() * (high - low + 1) + low);
}
function getElement(id, parent){
if (!elementConfig[id]){
return false;
}
var elem = !parent ? document.body : parent;
var elementArr = elementConfig[id];
for (var x in elementArr){
var pos = elementArr[x];
if (isNaN(pos*1)){ //dont know why, but for some reason after the last position it loops once again and "pos" is loaded with a function WTF. I got tired finding why and did this
continue;
}
if (!elem.childNodes[pos]){
return false;
}
elem = elem.childNodes[pos];
}
return elem;
}
function getLastMsg(){
var messages = document.querySelectorAll('.msg');
var pos = messages.length-1;
while (messages[pos] && (messages[pos].classList.contains('msg-system') || messages[pos].querySelector('.message-out'))){
pos--;
if (pos <= -1){
return false;
}
}
if (messages[pos] && messages[pos].querySelector('.selectable-text')){
return messages[pos].querySelector('.selectable-text').innerText;
} else {
return false;
}
}
function getUnreadChats(){
var unreadchats = [];
var chats = getElement("chats");
if (chats){
chats = chats.childNodes;
for (var i in chats){
if (!(chats[i] instanceof Element)){
continue;
}
var icons = getElement("chat_icons", chats[i]).childNodes;
if (!icons){
continue;
}
for (var j in icons){
if (icons[j] instanceof Element){
if (!(icons[j].childNodes[0].getAttribute('data-icon') == 'muted' || icons[j].childNodes[0].getAttribute('data-icon') == 'pinned')){
unreadchats.push(chats[i]);
break;
}
}
}
}
}
return unreadchats;
}
function didYouSendLastMsg(){
var messages = document.querySelectorAll('.msg');
if (messages.length <= 0){
return false;
}
var pos = messages.length-1;
while (messages[pos] && messages[pos].classList.contains('msg-system')){
pos--;
if (pos <= -1){
return -1;
}
}
if (messages[pos].querySelector('.message-out')){
return true;
}
return false;
}
// Call the main function again
const goAgain = (fn, sec) => {
// const chat = document.querySelector('div.chat:not(.unread)')
// selectChat(chat)
setTimeout(fn, sec * 1000)
}
// Dispath an event (of click, por instance)
const eventFire = (el, etype) => {
var evt = document.createEvent("MouseEvents");
evt.initMouseEvent(etype, true, true, window,0, 0, 0, 0, 0, false, false, false, false, 0, null);
el.dispatchEvent(evt);
}
// Select a chat to show the main box
const selectChat = (chat, cb) => {
const title = getElement("chat_title",chat).title;
eventFire(chat.firstChild.firstChild, 'mousedown');
if (!cb) return;
const loopFewTimes = () => {
setTimeout(() => {
const titleMain = getElement("selected_title").title;
if (titleMain !== undefined && titleMain != title){
console.log('not yet');
return loopFewTimes();
}
return cb();
}, 300);
}
loopFewTimes();
}
// Send a message
const sendMessage = (chat, message, cb) => {
//avoid duplicate sending
var title;
if (chat){
title = getElement("chat_title",chat).title;
} else {
title = getElement("selected_title").title;
}
ignoreLastMsg[title] = message;
messageBox = document.querySelectorAll("[contenteditable='true']")[0];
//add text into input field
messageBox.innerHTML = message.replace(/ /gm,'');
//Force refresh
event = document.createEvent("UIEvents");
event.initUIEvent("input", true, true, window, 1);
messageBox.dispatchEvent(event);
//Click at Send Button
eventFire(document.querySelector('span[data-icon="send"]'), 'click');
cb();
}
//
// MAIN LOGIC
//
const start = (_chats, cnt = 0) => {
// get next unread chat
const chats = _chats || getUnreadChats();
const chat = chats[cnt];
var processLastMsgOnChat = false;
var lastMsg;
if (!lastMessageOnChat){
if (false === (lastMessageOnChat = getLastMsg())){
lastMessageOnChat = true; //to prevent the first "if" to go true everytime
} else {
lastMsg = lastMessageOnChat;
}
} else if (lastMessageOnChat != getLastMsg() && getLastMsg() !== false && !didYouSendLastMsg()){
lastMessageOnChat = lastMsg = getLastMsg();
processLastMsgOnChat = true;
}
if (!processLastMsgOnChat && (chats.length == 0 || !chat)) {
console.log(new Date(), 'nothing to do now... (1)', chats.length, chat);
return goAgain(start, 3);
}
// get infos
var title;
if (!processLastMsgOnChat){
title = getElement("chat_title",chat).title + '';
lastMsg = (getElement("chat_lastmsg", chat) || { innerText: '' }).innerText; //.last-msg returns null when some user is typing a message to me
} else {
title = getElement("selected_title").title;
}
// avoid sending duplicate messaegs
if (ignoreLastMsg[title] && (ignoreLastMsg[title]) == lastMsg) {
console.log(new Date(), 'nothing to do now... (2)', title, lastMsg);
return goAgain(() => { start(chats, cnt + 1) }, 0.1);
}
// what to answer back?
let sendText
if (lastMsg.toUpperCase().indexOf('#HELP') > -1){
sendText = `
Cool ${title}! Some commands that you can send me:
1. *#TIME*
2. *#JOKE*`
}
if (lastMsg.toUpperCase().indexOf('#About') > -1){
sendText = `
Cool ${title}! Some commands that you can send me:
*${new Date()}*`
}
if (lastMsg.toUpperCase().indexOf('#TIME') > -1){
sendText = `
Don't you have a clock, dude?
*${new Date()}*`
}
if (lastMsg.toUpperCase().indexOf('#JOKE') > -1){
sendText = jokeList[rand(jokeList.length - 1)];
}
// that's sad, there's not to send back...
if (!sendText) {
ignoreLastMsg[title] = lastMsg;
console.log(new Date(), 'new message ignored -> ', title, lastMsg);
return goAgain(() => { start(chats, cnt + 1) }, 0.1);
}
console.log(new Date(), 'new message to process, uhull -> ', title, lastMsg);
// select chat and send message
if (!processLastMsgOnChat){
selectChat(chat, () => {
sendMessage(chat, sendText.trim(), () => {
goAgain(() => { start(chats, cnt + 1) }, 0.1);
});
})
} else {
sendMessage(null, sendText.trim(), () => {
goAgain(() => { start(chats, cnt + 1) }, 0.1);
});
}
}
start();