Javascript - Play audio only when a div is visible in the DOM - javascript

I have a problem with my script, and that is that I want to play an audio when I click on a .bbp button, but this button is inside a hidden div that is then cloned.
Only when the cloned div becomes visible in the DOM, I want to play an audio when I click on .bbp, but it does not work for me.
SEE DEMO LIVE (Codepen) - The Snippet does not run on Stackoverflow
Note that if you comment #products, the audio assigned to .bbp yes will play, otherwise it will NOT play, since the audio
script can not identify if #products is visible in the DOM or not.
So, first I need to know that .bbp is visible, and I can not find how I can do it.
Any idea...?
Thanks in advance!
//-----------------
HTML & CSS
#products {display:none}
#derecha {display:none}
<div class="comprar">Clone 1</div> <!--Clone the div from "products" to "derecha"-->
<div class="bbp">X</div> <!--Delete the cloned div placed into "derecha"-->
SCRIP (Play Audio)
let audioHolderComprar = {};
$('.comprar').click(()=>{
let tempIdentifier = Date.now();
audioHolderComprar[tempIdentifier] = new Audio('comprar.mp3');
audioHolderComprar[tempIdentifier].play();
setTimeout(() => {
delete audioHolderComprar[tempIdentifier];
}, audioHolderComprar[tempIdentifier].duration + 1200);
});
//------------------
let audioHolderBorrar = {};
$('.bbp').click(()=>{
let tempIdentifier = Date.now();
audioHolderBorrar[tempIdentifier] = new Audio('borrar.mp3');
audioHolderBorrar[tempIdentifier].play();
setTimeout(() => {
delete audioHolderBorrar[tempIdentifier];
}, audioHolderBorrar[tempIdentifier].duration + 1200);
});

As I've mentioned in my comment, you have two places where you handle the click event for .bpp - these interfere with each other.
Also you're mixing the places where you should add html and javascript code. Though it works, it's a little bit messy.
Replace all of the content in your HTML pane on the left by this:
<div id="container">
<div id="productos">
<!-- =============== -->
<div id="cont-p1" class="cont-p">
<div id="producto-1">
<div class="img-prod"><img src="https://upload.wikimedia.org/wikipedia/commons/3/39/Lichtenstein_img_processing_test.png"></div>cont-p1 cloned!<br><br>Input Value = 1</div>
<input class="add-prod" type="num" value="1">
<div class="bbp">X</div></div>
</div> <!-- // productos -->
<div class="derecha" id="derecha"></div> <!-- // div derecha -->
<div id="comp-p1" data-clone="cont-p1" class="comp-clone comprar">Clone 1</div>
<div class="cont-num" id="clicks">0</div>
<div class="cont-num" id="clicksdos">0</div>
<div id="cont-resultado">
<input name="total" id="total">
</div>
<div id="cont-note">How to play the audio on the button to close the cloned div <span>.bbp</span><br>( <span class="red">X</span> ),<br>if the audio script can not know that it has been cloned...?
<br><br>
Note the CSS (line 3) that the div container of the all divĀ“s that must be cloned is in <span>display=none</span>, but if you comment this line it can reproduce the audio onclick in the X button</div>
</div> <!-- // container -->
and all of the following goes into the JS pane to the right:
/*
https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js
*/
let audioHolderComprar = {};
$('.comprar').click(()=>{
let tempIdentifier = Date.now();
audioHolderComprar[tempIdentifier] = new Audio('https://notificationsounds.com/soundfiles/8b16ebc056e613024c057be590b542eb/file-sounds-1113-unconvinced.mp3');
audioHolderComprar[tempIdentifier].play();
// removing after play process gets over so if won't consume memory
setTimeout(() => {
delete audioHolderComprar[tempIdentifier];
}, audioHolderComprar[tempIdentifier].duration + 1200 /* you can remove threshold value if you wants to */);
});
//------------------
let audioHolderBorrar = {};
let clicks = 0;
let clicksdos = 0;
const safeInt = (key) => {
let value = parseInt(getValue(key));
return (isNaN(value) || value < 0) ? 0 : value;
}
// This loads our clicks from the LocalStorage
const loadClicks = () => {
clicks = safeInt('clicks');
clicksdos = safeInt('clicksdos');
}
const loadHTML = () => {
return getValue('html', '');
}
const loadFromStorage = () => {
let html = loadHTML();
if (html !== '') {
loadClicks();
}
displayClicks();
document.querySelector(".derecha").innerHTML = html;
}
// Display the clicks on the screen
const displayClicks = () => {
clicks = (clicks === NaN) ? 0 : clicks;
clicksdos = (clicksdos === NaN) ? 0 : clicksdos;
document.querySelector('#clicks').innerHTML = clicks;
document.querySelector('#clicksdos').innerHTML = clicksdos;
// Hide / Show Result
let display = (clicks > 0) ? 'block' : 'none';
document.querySelector('#cont-resultado').style.display = display;
document.querySelector('.derecha').style.display = display;
//document.querySelector('#aviso-producto-agregado').style.display = "block";
}
const adjustClicks = (value) => {
clicks += value;
clicksdos += value;
storeValue('clicks', clicks);
storeValue('clicksdos', clicksdos);
displayClicks();
}
const addClick = () => adjustClicks(1);
const removeClick = () => adjustClicks(-1);
// Manage localStorage
const storeValue = (key, value) => (localStorage) ? localStorage.setItem(key, value) : '';
const getValue = (key, defaultValue) => (localStorage) ? localStorage.getItem(key) : defaultValue;
const storeHTML = () => storeValue("html", document.getElementsByClassName("derecha")[0].innerHTML);
// Add a node to the Derecha
const addToDerecha = (nodeId) => {
let node = document.querySelector(`#${nodeId}`);
document.querySelector('.derecha').appendChild(node.cloneNode(true));
storeHTML();
displaySuma();
};
// Monitor ALL click events
document.addEventListener('click', (event) => {
let target = event.target;
// Add
if (target.matches('.comp-clone')) {
addClick();
addToDerecha(event.target.dataset.clone);
}
// Remove
if (target.matches('.bbp')) {
let tempIdentifier = Date.now();
audioHolderBorrar[tempIdentifier] = new Audio('https://notificationsounds.com/soundfiles/99c5e07b4d5de9d18c350cdf64c5aa3d/file-sounds-1110-stairs.mp3');
audioHolderBorrar[tempIdentifier].play();
// removing after play process gets over so if won't consume memory
setTimeout(() => {
delete audioHolderBorrar[tempIdentifier];
}, audioHolderBorrar[tempIdentifier].duration + 1200 /* you can remove threshold value if you wants to */);
getParent('.derecha', target).removeChild(target.parentNode);
removeClick();
storeHTML();
displaySuma();
}
});
// This is just a helper function.
const getParent = (match, node) => (node.matches(match)) ? node : getParent(match, node.parentNode);
// New Script for sum inputs
//const displaySuma = () => document.getElementById("total").value = suma();
const displaySuma=()=>document.getElementById("total").value=suma().toLocaleString("es-ES");
const suma = function() {
return Array.from(document.querySelectorAll(".derecha div .add-prod"))
.reduce((a, v) => a + parseFloat(v.value), 0);
}
// Code to run when the document loads.
document.addEventListener('DOMContentLoaded', () => {
if (localStorage) {
loadFromStorage();
}
displaySuma();
});
</script>
<script>
// Displays the new product alert added when the scroll is detected in the div #derecha
var displaced = document.getElementById('derecha')
if (displaced.scrollHeight > displaced.offsetHeight) {
document.getElementById("notice-product-added").style.display = "block";
};
// LocalStorage for the div #notice-product-added
const showMsgCart=localStorage.getItem('showMsgCarrito');if(showMsgCart==='false'){$('#notice-product-added').hide();}$('#notice-product-added').on('click',function(){$('#notice-product-added').fadeOut('slow');localStorage.setItem('showMsgCarrito','false');});
After that you should hear the closing sound.

Related

How to make Drag and Drop work Horizontally?

I'm trying to get Drag and Drop to work correctly
But in the image gallery, it only changes the position of the photo when dragging it under the other image
As if it only works vertically
Demo
Javascript Vanilla
const enableDragSort = className => {
// Gets all child elements of the .gallery or .datasheet class
[...document.getElementsByClassName(className)].forEach(enableDragList);
}
const enableDragList = className => {
// For each child of the class, add the draggable attribute
[...className.children].forEach(enableDragItem);
}
const enableDragItem = item => {
item.setAttribute('draggable', true);
item.ondrag = handleDrag;
item.ondragend = handleDrop;
}
const handleDrag = event => {
const item = event.target.closest('[draggable]');
item.classList.add('drag-sort-active');
// .gallery or .datasheet
const className = item.parentElement;
const x = event.clientX;
const y = event.clientY;
let swap = document.elementFromPoint(x, y) ?? item;
if (className === swap.parentElement) {
swap = (swap !== item.nextSibling) ? swap : swap.nextSibling;
className.insertBefore(item, swap);
}
}
const handleDrop = ({ target }) => {
const item = target.closest('[draggable]');
item.classList.remove('drag-sort-active');
}
// Drag Drop
enableDragSort('gallery'); // It does not work properly
enableDragSort('datasheet'); // It works

How to use 'timeupdate' event listener to highlight text from audio?

I'm using the 'timeupdate' event listener to sync a subtitle file with audio.
It is working currently, but I'd like to adjust it to where it is just highlighting the corresponding sentence in a large paragraph instead of deleting the entire span and replacing it with just the current sentence. This is the sort of functionality I am trying to replicate: https://j.hn/lab/html5karaoke/dream.html (see how it only highlights the section that it is currently on).
This is made complicated due to timeupdate constantly checking multiple times a second.
Here is the code:
var audioSync = function (options) {
var audioPlayer = document.getElementById(options.audioPlayer);
var subtitles = document.getElementById(options.subtitlesContainer);
var syncData = [];
var init = (function () {
return fetch(new Request(options.subtitlesFile))
.then((response) => response.text())
.then(createSubtitle);
})();
function createSubtitle(text) {
var rawSubTitle = text;
convertVttToJson(text).then((result) => {
var x = 0;
for (var i = 0; i < result.length; i++) {
if (result[i].part && result[i].part.trim() != '') {
syncData[x] = result[i];
x++;
}
}
});
}
audioPlayer.addEventListener('timeupdate', function (e) {
syncData.forEach(function (element, index, array) {
if (
audioPlayer.currentTime * 1000 >= element.start &&
audioPlayer.currentTime * 1000 <= element.end
) {
while (subtitles.hasChildNodes()) {
subtitles.removeChild(subtitles.firstChild);
}
var el = document.createElement('span');
el.setAttribute('id', 'c_' + index);
el.innerText = syncData[index].part + '\n';
el.style.background = 'yellow';
subtitles.appendChild(el);
}
});
});
};
new audioSync({
audioPlayer: 'audiofile', // the id of the audio tag
subtitlesContainer: 'subtitles', // the id where subtitles should show
subtitlesFile: './sample.vtt', // the path to the vtt file
});

Add Bullets to Each New Line within a textarea

I have a textarea that I want users be able to input text in. Each new line within the textarea will eventually get split up and sent back to the database to be used elsewhere. To show this from a users perspective, I want to add a bullet to each new line that they enter within the textarea.
I've got this working to the point where it successfully adds a bullet when you press enter and are on the last line of the textarea content
<textarea onInput="handleInput(event)" rows="10"></textarea>
let previousLength = 0;
const handleInput = (event) => {
const bullet = "\u2022";
const newLength = event.target.value.length;
const characterCode = event.target.value.substr(-1).charCodeAt(0);
if (newLength > previousLength) {
if (characterCode === 10) {
event.target.value = `${event.target.value}${bullet} `;
} else if (newLength === 1) {
event.target.value = `${bullet} ${event.target.value}`;
}
}
previousLength = newLength;
}
https://codepen.io/andrewgarrison/pen/vqqmMv?editors=1010
However, I'd also like for it to add a bullet when you are in the middle of the textarea content and you press enter. Right now it just adds a new line with no bullet.
You can find the current position within the text area, and when enter is pressed, append a new line and a bullet:
const bullet = "\u2022";
const bulletWithSpace = `${bullet} `;
const enter = 13;
const handleInput = (event) => {
const { keyCode, target } = event;
const { selectionStart, value } = target;
if (keyCode === enter) {
console.log('a');
target.value = [...value]
.map((c, i) => i === selectionStart - 1
? `\n${bulletWithSpace}`
: c
)
.join('');
console.log(target.value);
target.selectionStart = selectionStart+bulletWithSpace.length;
target.selectionEnd = selectionStart+bulletWithSpace.length;
}
if (value[0] !== bullet) {
target.value = `${bulletWithSpace}${value}`;
}
}
<textarea onkeyup="handleInput(event)" rows="10"></textarea>
For anyone else who come accross this problem, here is how I did it:
.text-area {
display: list-item;
margin-left : 1em;
outline: none;
}
.text-area div {
display: list-item;
}
<div class="text-area" contenteditable="true">
</div>

JavaScript button that generates objects in order

i created a button in JS that generates GIFs randomly,
i would like to change that by making them appear in order and then repeat.
window.onload = () => {
const factsArr = [
{
image:'https://media.giphy.com/media/P6lX0T2MzYpdC/giphy.gif',
},
{
image:'https://media.giphy.com/media/uWdVKl2xN1eVi/giphy.gif',
},
{
image:'https://media.giphy.com/media/1nkUav308CBws/giphy.gif',
}
];
document.getElementById('generate-btn').addEventListener('click', () => {
const idx = Math.floor(Math.random() * factsArr.length);
document.getElementById('image').setAttribute('src', factsArr[idx].image)
})
}
<button id="generate-btn">Amazing Fact Button</button>
<img id="image"></img>
In the snippet below, I used a global currImage variable which increases on each click and resets when reaching the image array size. The image is taken from the array's currImage index.
// Init the variable
var currImage = 0;
window.onload = () => {
const factsArr = [
{ image:'https://media.giphy.com/media/P6lX0T2MzYpdC/giphy.gif'},
{ image:'https://media.giphy.com/media/uWdVKl2xN1eVi/giphy.gif'},
{ image:'https://media.giphy.com/media/1nkUav308CBws/giphy.gif'}
];
document.getElementById('generate-btn').addEventListener('click', () =>
{
document.getElementById('image').setAttribute('src', factsArr[currImage].image);
// increment the variable on each click
currImage++;
//reset the variable when it reaches the image array size
if (currImage == factsArr.length)
currImage = 0;
})
}
<button id="generate-btn">Amazing Fact Button</button>
<img id="image"></img>
Introduce a numeric variable to indicate the current index of the array to use.
const factsArr = [
{image:'https://media.giphy.com/media/P6lX0T2MzYpdC/giphy.gif'},
{image:'https://media.giphy.com/media/uWdVKl2xN1eVi/giphy.gif'},
{image:'https://media.giphy.com/media/1nkUav308CBws/giphy.gif'}
];
const tot = factsArr.length;
let c = 0;
document.getElementById('generate-btn').addEventListener('click', () => {
document.getElementById('image').src = factsArr[c++ % tot].image;
});
<button id="generate-btn">Amazing Fact Button</button><br>
<img id="image">

Change appearance of elements based on sequence stored in array

I have two quarters of a circle in two span tags:
<div>
<span id="topleft"></span>
<span id="topright"></span>
</div>
<button id = 'clickme' style = 'margin-top: 100px;'>CLICK ME</button>
When I click the button I want the divs to 'light up' based on a sequence stored in an array using JavaScript:
let reset = (position) => {
document.getElementById(position).style.opacity = 1;
};
let blink = (position) => {
document.getElementById(position).style.opacity = 0.5;
};
let sequence = (positions) => {
let currentBlink = positions.pop();
blink(currentBlink);
setTimeout(reset.bind(null, currentBlink), 1000);
if (positions.length > 0) {
sequence(positions);
}
}
document.getElementById('clickme').onclick = () => {
sequence(positions);
}
let positions = ['topleft', 'topright'];
The problem is that both divs 'light up' at the same time where I actually want a pause between the two.
How can I achieve this effect?
Thanks any help appreciated.
Invoke the function after timeout in callback.
let reset = (position) => {
document.getElementById(position).style.opacity = 1;
};
let blink = (position) => {
document.getElementById(position).style.opacity = 0.5;
};
let sequence = (positions) => {
let currentBlink = positions.pop();
blink(currentBlink);
setTimeout(function() {
reset(currentBlink);
if (positions.length > 0) {
sequence(positions);
}
}, 1000);
}
document.getElementById('clickme').onclick = () => {
sequence(positions);
}
let positions = ['topleft', 'topright'];
<div>
<span id="topleft">aaa</span>
<span id="topright">aaa</span>
</div>
<button id='clickme' style='margin-top: 100px;'>CLICK ME</button>
Fiddle Demo

Categories

Resources