JavaScript changes to css are not being being kept - javascript

The do_game function is supposed to change the color of the body permanently, but instead it happens for an instant and goes back the way that it was.
let do_game = ()=>{
let colorArray = ["blue","cyan","gray","green","magenta","orange",
,"white","yellow"]
// let target = colorArray[Math.floor(Math.random() * 8)];
let target = colorArray[0];
console.log(target);
let input = validateValue(colorArray);
if(input === false){
return false;
}
if(compareInputToTarget(input, target) == false){
return false;
}
let body = document.getElementById("body");
body.style.background = target;
// window.backgroundTarget = target;
return target;
}
(function(){
document.getElementById("ok").addEventListener("click", do_game);
// body.style.background = window.backgroundTarget;
})()

Cannot reproduce based on the code you provided. The following adjustment to your code demonstrates a permanent change of color.
function validateValue() {
return true;
}
function compareInputToTarget(input, target) {
return true;
}
let do_game = ()=>{
let colorArray = ["blue","cyan","gray","green","magenta","orange",
,"white","yellow"]
// let target = colorArray[Math.floor(Math.random() * 8)];
let target = colorArray[0];
console.log(target);
let input = validateValue(colorArray);
if(input === false){
return false;
}
if(compareInputToTarget(input, target) == false){
return false;
}
let body = document.getElementById("body");
body.style.background = target;
// window.backgroundTarget = target;
return target;
}
(function(){
document.getElementById("ok").addEventListener("click", do_game);
// body.style.background = window.backgroundTarget;
})()
<body id="body">
<button id="ok">OK</button>
</body>

Related

JavaScript: How can I set src in the JavaScript code based on data attributes in the HTML code?

I want to use 6 of these Before/After Audio Players on my website: https://github.com/mattbartley/AB-Audio-Player
However, the sources of the audio files are set in the JavaScript code. So when I implement multiple instances of this player in my HTML, they all play the same two audio files. I want to set data attributes in the HTML code in each player div for the src of the files and let the JavaScript use that instead. How do I go about it?
<div class="player__wrapper" data-audio-before="Song-1-before.mp3" data-audio-after="Song-1-after.mp3">
...
<div class="player__wrapper" data-audio-before="Song-2-before.mp3" data-audio-after="Song-2-after.mp3">
...
And so on. This would be the updated HTML code.
I think it's a pretty basic solution, I'm just not good in JavaScript.
Here is the current JavaScript code:
//Set up audio elements
var soundA = document.createElement("audio");
//Set audio A src here
soundA.src = "./assets/a.mp3";
soundA.preload = "auto";
soundA.setAttribute("hidden", "true");
soundA.setAttribute("onplaying", "stepA()");
document.body.append(soundA);
var soundB = document.createElement("audio");
//Set audio B src here
soundB.src = "./assets/b.mp3";
soundB.preload = "auto";
soundB.setAttribute("hidden", "true");
soundB.setAttribute("onplaying", "stepB()");
document.body.append(soundB);
I tried something like this: Hover over div, use its data-attribute to change the src of an img
And other approaches as well.
Couldn't get it to work.
I forked the
Matt Bartley 's AB-Audio-Player - not great but it works with multiple instances:
As commented, the player initialization is done within a forEach loop.
All elements are selected by class names to avoid non unique IDs and relative element selection.
So you need to update your HTML template accordingly.
let players = document.querySelectorAll(".player__wrapper");
initPlayers(players);
function initPlayers(players) {
players.forEach((player) => {
//Get button elements
const playBtns = player.querySelectorAll(".ab__button");
const aButton = player.querySelector(".a__button");
const bButton = player.querySelector(".b__button");
const playButton = player.querySelector(".play__button");
const stopButton = player.querySelector(".stop__button");
const progressBar = player.querySelector(".progress__bar");
const progressFill = player.querySelector(".progress__fill");
// set icons
const playIcon = '<i class="fa-solid fa-play"></i>';
const pauseIcon = '<i class="fa-solid fa-pause"></i>';
const stopIcon = '<i class="fa-solid fa-stop"></i>';
//Default loading state for each sound
var soundAReady = false;
var soundBReady = false;
//Set up audio elements
var soundA = document.createElement("audio");
soundA.src = player.getAttribute("data-sound-a");
soundA.preload = "auto";
soundA.setAttribute("hidden", "true");
player.append(soundA);
var soundB = document.createElement("audio");
soundB.src = player.getAttribute("data-sound-b");
soundB.preload = "auto";
soundB.setAttribute("hidden", "true");
player.append(soundB);
//playSoundA
aButton.addEventListener("click", (e) => {
pauseAll();
playButton.innerHTML = pauseIcon;
aButton.disabled = true;
bButton.disabled = false;
stopButton.disabled = false;
soundA.currentTime = soundB.currentTime;
soundA.play();
});
//playSoundB
bButton.addEventListener("click", (e) => {
pauseAll();
playButton.innerHTML = pauseIcon;
bButton.disabled = true;
aButton.disabled = false;
stopButton.disabled = false;
soundB.currentTime = soundA.currentTime;
soundB.play();
});
//playSoundA
soundA.addEventListener("playing", (e) => {
console.log("playing");
progressFill.style.width =
((soundA.currentTime / soundA.duration) * 100 || 0) + "%";
requestAnimationFrame(stepA);
});
//playSoundB
soundB.addEventListener("playing", (e) => {
console.log("playing B");
progressFill.style.width =
((soundB.currentTime / soundB.duration) * 100 || 0) + "%";
requestAnimationFrame(stepB);
});
// playPause
playButton.addEventListener("click", (e) => {
if (soundA.paused & soundB.paused) {
let soundATime = soundA.currentTime;
let soundBTime = soundB.currentTime;
if (soundATime >= soundBTime) {
soundA.play();
bButton.disabled = false;
aButton.disabled = true;
playButton.innerHTML = pauseIcon;
} else {
soundB.play();
bButton.disabled = true;
aButton.disabled = false;
playButton.innerHTML = pauseIcon;
}
stopButton.disabled = false;
} else {
playButton.innerHTML = playIcon;
soundA.pause();
soundB.pause();
}
});
// stop
stopButton.addEventListener("click", (e) => {
playButton.innerHTML = playIcon;
aButton.disabled = false;
bButton.disabled = true;
playButton.disabled = false;
stopButton.disabled = true;
soundA.pause();
soundA.currentTime = 0;
soundB.pause();
soundB.currentTime = 0;
});
//Check for mobile to enable audio playback without waiting for download status.
if (
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
navigator.userAgent
)
) {
playButton.disabled = false;
}
//Default loading state for each sound
var soundAReady = false;
var soundBReady = false;
//When audio can play through (loaded), run the function to enable buttons
//The canplaythrough event will fire every time the audio switches, so the !soundA/BReady
//prevents additional checks
soundA.oncanplaythrough = function() {
if (!soundAReady) {
soundAReady = true;
audioIsReady();
}
};
soundB.oncanplaythrough = function() {
if (!soundBReady) {
soundBReady = true;
audioIsReady();
}
};
// Check if both A & B are ready and enable the correct buttons
function audioIsReady() {
if (soundAReady && soundBReady) {
console.log("...audio loaded!");
aButton.disabled = false;
playButton.disabled = false;
} else {
console.log("Audio loading...");
}
}
const progress = player.querySelector(".progress");
// Listen for click on entire progress bar div (to allow skipping ahead)
progress.addEventListener("click", function(event) {
// Get X coordinate of click in div
var rect = this.getBoundingClientRect();
// Convert click position to percentage value
var percentage = (event.clientX - rect.left) / this.offsetWidth;
// Seek to the percentage converted to seconds
soundA.currentTime = percentage * soundA.duration;
soundB.currentTime = percentage * soundB.duration;
});
//Frame animations for progress bar fill - converts to CSS percentage
function stepA() {
progressFill.style.width =
((soundA.currentTime / soundA.duration) * 100 || 0) + "%";
requestAnimationFrame(stepA);
}
function stepB() {
progressFill.style.width =
((soundB.currentTime / soundB.duration) * 100 || 0) + "%";
requestAnimationFrame(stepB);
}
//Play/Stop correct audio and toggle A/B, Play/Pause, and Stop buttons
function playPause() {
if (soundA.paused & soundB.paused) {
let soundATime = soundA.currentTime;
let soundBTime = soundB.currentTime;
if (soundATime >= soundBTime) {
soundA.play();
bButton.disabled = false;
aButton.disabled = true;
playButton.innerHTML = pauseIcon;
} else {
soundB.play();
bButton.disabled = true;
aButton.disabled = false;
playButton.innerHTML = pauseIcon;
}
stopButton.disabled = false;
} else {
playButton.innerHTML = playIcon;
soundA.pause();
soundB.pause();
}
}
// optional: set auto ids
let allAudio = document.querySelectorAll("audio");
allAudio.forEach((audio, i) => {
audio.id = "audio_" + i;
});
// rewind all at end
allAudio.forEach((audio) => {
//audio.pause();
audio.addEventListener("ended", (e) => {
audio.currentTime = 0;
progressFill.style.width = "0%";
});
});
function pauseAll() {
let allAudio = document.querySelectorAll("audio");
allAudio.forEach((audio) => {
audio.pause();
});
}
});
}
<link href="https://fonts.googleapis.com/css2?family=Lato:wght#400;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" integrity="sha512-KfkfwYDsLkIlwQp6LFnl8zNdLGxu9YAA1QvwINks4PhcElQSvqcyVLLD9aMhXd13uQjoXtEKNosOWaZqXgel0g==" crossorigin="anonymous" referrerpolicy="no-referrer"
/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/mattbartley/AB-Audio-Player#main/css/style.css" />
<div class="player__wrapper" data-sound-a="https://cdn.jsdelivr.net/gh/mattbartley/AB-Audio-Player#main/assets/a.mp3" data-sound-b="https://cdn.jsdelivr.net/gh/mattbartley/AB-Audio-Player#main/assets/b.mp3">
<div class="progress__container progress">
<div class="progress__bar progress__fill"></div>
</div>
<div class="ab__controls">
<button class="ab__button a__button" disabled="true">
A
</button>
<button class="ab__button b__button" disabled="true">
B
</button>
</div>
<div class="play__stop__controls">
<button class="play__pause__button play__button" disabled="true">
<i class="fa-solid fa-play"></i>
</button>
<button class="play__pause__button stop__button" disabled="true">
<i class="fa-solid fa-stop"></i>
</button>
</div>
</div>
<div class="player__wrapper" data-sound-a="https://cdn.jsdelivr.net/gh/mattbartley/AB-Audio-Player#main/assets/a.mp3" data-sound-b="https://cdn.jsdelivr.net/gh/mattbartley/AB-Audio-Player#main/assets/b.mp3">
<div class="progress__container progress">
<div class="progress__bar progress__fill"></div>
</div>
<div class="ab__controls">
<button class="ab__button a__button" disabled="true">
A
</button>
<button class="ab__button b__button" disabled="true">
B
</button>
</div>
<div class="play__stop__controls">
<button class="play__pause__button play__button" disabled="true">
<i class="fa-solid fa-play"></i>
</button>
<button class="play__pause__button stop__button" disabled="true">
<i class="fa-solid fa-stop"></i>
</button>
</div>
</div>
According to MDN, firstly you should change your HTML a bit and add id to your elements:
<div id="player-1" class="player__wrapper" data-audio-before="Song-1-before.mp3" data-audio-after="Song-1-after.mp3">
Then you can access your data-audio attribute from your JS code thorough dataset object:
//Set up audio elements
var soundA = document.createElement("audio");
//bring div element to your code
const playerA = document.querySelector("#player-1")
//Set audio A src here
soundA.src = playerA.dataset.dataAudioBefore;
soundA.preload = "auto";
soundA.setAttribute("hidden", "true");
soundA.setAttribute("onplaying", "stepA()");
document.body.append(soundA);
var soundB = document.createElement("audio");
//Set audio B src here
soundB.src = playerA.dataset.dataAudioAfter;
soundB.preload = "auto";
soundB.setAttribute("hidden", "true");
soundB.setAttribute("onplaying", "stepB()");
document.body.append(soundB);
hope this helps!
This is actually a comment on your answer, but duo to StackOverflow policy, I couldn't comment directly!
Anyway, I think your problem is that you are using querySelectorAll with classes.
Have you tried using id in your HTML element and using querySelector("#id") in your JS code?
In addition, please vote me up/check correct answer if it helps!
I got it working somewhat with some help from ChatGPT:
const players = document.querySelectorAll('.player__wrapper');
var soundA, soundB;
players.forEach(player => {
soundA = document.createElement("audio");
soundA.src = player.dataset.audioBefore;
soundA.preload = "auto";
soundA.setAttribute("hidden", "true");
soundA.setAttribute("onplaying", "stepA()");
document.body.append(soundA);
soundB = document.createElement("audio");
soundB.src = player.dataset.audioAfter;
soundB.preload = "auto";
soundB.setAttribute("hidden", "true");
soundB.setAttribute("onplaying", "stepB()");
document.body.append(soundB);
});
It works when I only have one audio player. When I use two, the first one uses the audio files which I put in the second one and the second one doesn't work at all.
Anyone got an idea? Maybe the rest of the JS code is relevant too, here it is:
//Get button elements
const aButton = document.getElementById("a__button");
const bButton = document.getElementById("b__button");
const playButton = document.getElementById("play__button");
const stopButton = document.getElementById("stop__button");
const progressBar = document.getElementById("progress__bar");
const progressFill = document.getElementById("progress__fill");
const playIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M3 22v-20l18 10-18 10z"/></svg>';
const pauseIcon = '<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd"><path d="M10 24h-6v-24h6v24zm10 0h-6v-24h6v24zm-11-23h-4v22h4v-22zm10 0h-4v22h4v-22z"/></svg>';
const stopIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M2 2h20v20h-20z"/></svg>';
//Check for mobile to enable audio playback without waiting for download status.
if (
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
navigator.userAgent
)
) {
playButton.disabled = false;
}
//Default loading state for each sound
var soundAReady = false;
var soundBReady = false;
//When audio can play through (loaded), run the function to enable buttons
//The canplaythrough event will fire every time the audio switches, so the !soundA/BReady
//prevents additional checks
soundA.oncanplaythrough = function () {
if (!soundAReady) {
soundAReady = true;
audioIsReady();
}
};
soundB.oncanplaythrough = function () {
if (!soundBReady) {
soundBReady = true;
audioIsReady();
}
};
// Check if both A & B are ready and enable the correct buttons
function audioIsReady() {
if (soundAReady && soundBReady) {
console.log("...audio loaded!");
aButton.disabled = false;
playButton.disabled = false;
} else {
console.log("Audio loading...");
}
}
const progress = document.getElementById("progress");
// Listen for click on entire progress bar div (to allow skipping ahead)
progress.addEventListener("click", function (event) {
// Get X coordinate of click in div
var rect = this.getBoundingClientRect();
// Convert click position to percentage value
var percentage = (event.clientX - rect.left) / this.offsetWidth;
// Seek to the percentage converted to seconds
soundA.currentTime = percentage * soundA.duration;
soundB.currentTime = percentage * soundB.duration;
});
//Frame animations for progress bar fill - converts to CSS percentage
function stepA() {
progressFill.style.width =
((soundA.currentTime / soundA.duration) * 100 || 0) + "%";
requestAnimationFrame(stepA);
}
function stepB() {
progressFill.style.width =
((soundB.currentTime / soundB.duration) * 100 || 0) + "%";
requestAnimationFrame(stepB);
}
//Play/Stop correct audio and toggle A/B, Play/Pause, and Stop buttons
const playPause = () => {
if (soundA.paused & soundB.paused) {
let soundATime = soundA.currentTime;
let soundBTime = soundB.currentTime;
if (soundATime >= soundBTime) {
soundA.play();
bButton.disabled = false;
aButton.disabled = true;
playButton.innerHTML = pauseIcon;
} else {
soundB.play();
bButton.disabled = true;
aButton.disabled = false;
playButton.innerHTML = pauseIcon;
}
stopButton.disabled = false;
} else {
playButton.innerHTML = playIcon;
soundA.pause();
soundB.pause();
}
};
const playSoundA = () => {
playButton.innerHTML = pauseIcon;
aButton.disabled = true;
bButton.disabled = false;
stopButton.disabled = false;
if (soundB.currentTime > 0) {
soundA.currentTime = soundB.currentTime;
soundA.play();
soundB.pause();
} else {
soundA.play();
soundB.pause();
}
};
const playSoundB = () => {
playButton.innerHTML = pauseIcon;
bButton.disabled = true;
aButton.disabled = false;
stopButton.disabled = false;
if (soundA.currentTime > 0) {
soundB.currentTime = soundA.currentTime;
soundB.play();
soundA.pause();
} else {
soundB.play();
}
};
const stopSounds = () => {
playButton.innerHTML = playIcon;
aButton.disabled = false;
bButton.disabled = true;
playButton.disabled = false;
stopButton.disabled = true;
soundA.pause();
soundA.currentTime = 0;
soundB.pause();
soundB.currentTime = 0;
};

Self made text-to-speech in javascript doesn't work properly

We made a text-to-speech function in javascript. The only problem now is that it doesn't work properly. When the play button is pressed, it is supposed to tell everything that's within the body tags. The problem is that most of the times it's not working and when it does, it's telling also the javascript code which it's outside of the body tag. How can i fix this so that it's working everytime the play button is pressed and it's only telling everything in the body tag?
onload = function() {
if ('speechSynthesis' in window) with(speechSynthesis) {
var playEle = document.querySelector('#play');
var pauseEle = document.querySelector('#pause');
var stopEle = document.querySelector('#stop');
var flag = false;
playEle.addEventListener('click', onClickPlay);
pauseEle.addEventListener('click', onClickPause);
stopEle.addEventListener('click', onClickStop);
function onClickPlay() {
if (!flag) {
flag = true;
utterance = new SpeechSynthesisUtterance(document.querySelector('body').textContent);
utterance.lang = 'nl-NL';
utterance.rate = 0.7;
utterance.onend = function() {
flag = false;
playEle.className = pauseEle.className = '';
stopEle.className = 'stopped';
};
playEle.className = 'played';
stopEle.className = '';
speak(utterance);
}
if (paused) {
playEle.className = 'played';
pauseEle.className = '';
resume();
}
}
function onClickPause() {
if (speaking && !paused) {
pauseEle.className = 'paused';
playEle.className = '';
pause();
}
}
function onClickStop() {
if (speaking) {
stopEle.className = 'stopped';
playEle.className = pauseEle.className = '';
flag = false;
cancel();
}
}
}
else { /* speech synthesis not supported */
msg = document.createElement('h5');
msg.textContent = "Detected no support for Speech Synthesis";
msg.style.textAlign = 'center';
msg.style.backgroundColor = 'red';
msg.style.color = 'white';
msg.style.marginTop = msg.style.marginBottom = 0;
document.body.insertBefore(msg, document.querySelector('div'));
}
}
<button id=play>Play</button>
<button id=pause>Pause</button>
<button id=stop>Stop</button>
Define the DIV you want the Speech recognition to read from:
</head>
<body>
<div>
<button id=play>Play</button>
<button id=pause>Pause</button>
<button id=stop>Stop</button>
</div>
<div id="readFrom">
Just a test o my friend mplungjan
</div>
<script type="text/javascript">
window.onload = function () {
if ('speechSynthesis' in window)
with (speechSynthesis) {
var playEle = document.querySelector('#play');
var pauseEle = document.querySelector('#pause');
var stopEle = document.querySelector('#stop');
var flag = false;
playEle.addEventListener('click', onClickPlay);
pauseEle.addEventListener('click', onClickPause);
stopEle.addEventListener('click', onClickStop);
function onClickPlay() {
if (!flag) {
flag = true;
utterance = new SpeechSynthesisUtterance(document.querySelector('#readFrom').innerHTML);
utterance.lang = 'nl-NL';
utterance.rate = 0.7;
utterance.onend = function () {
flag = false;
playEle.className = pauseEle.className = '';
stopEle.className = 'stopped';
};
playEle.className = 'played';
stopEle.className = '';
speak(utterance);
}
if (paused) {
playEle.className = 'played';
pauseEle.className = '';
resume();
}
}
function onClickPause() {
if (speaking && !paused) {
pauseEle.className = 'paused';
playEle.className = '';
pause();
}
}
function onClickStop() {
if (speaking) {
stopEle.className = 'stopped';
playEle.className = pauseEle.className = '';
flag = false;
cancel();
}
}
}
else { /* speech synthesis not supported */
msg = document.createElement('h5');
msg.textContent = "Detected no support for Speech Synthesis";
msg.style.textAlign = 'center';
msg.style.backgroundColor = 'red';
msg.style.color = 'white';
msg.style.marginTop = msg.style.marginBottom = 0;
document.body.insertBefore(msg, document.querySelector('div'));
}
}
</script>
</body>
It should be very simple. I see on your line that you are querying all the body text Content but that should not be it. Go on your JS Console and query: document.querySelector('body').textContent
You should see exactly what you are passing as arguments to:
utterance = new SpeechSynthesisUtterance(document.querySelector('body').textContent);
So now it's up to you, you would have to filter what you want to be read by putting it in a specific DIV or stripping HTML from the page according to a complex logic keeping just what you want to read.

Toggling between Boolean variable

I'm wondering how you would toggle between two boolean variables. This works correctly the first time running the code but then after running it a second time the output isn't correct.
Output first time running switchPlayer():
player1.isActive = false,
player2.isActive = true
Output second time running switchPlayer():
player1.isActive = true,
player2.isActive = true
Below is the code I wrote:
var Player = function(score, isActive){
this.score = score;
this.isActive = isActive;
}
Player.prototype.toggleIsActive = function(){
if(this.isActive === false){
this.isActive = true;
} else{
this.isActive = false;
}
}
function switchPlayer(){
if(player1.isActive === true){
player1.toggleIsActive();
player2.toggleIsActive();
} else{
player1.isActive = true;
}
}
var player1 = new Player("0", true);
var player2 = new Player("0", false);
switchPlayer();
switchPlayer();
You can simplify it like this:
Player.prototype.toggleIsActive = function(){
this.isActive = !this.isActive;
}
function switchPlayer(){
player1.toggleIsActive();
player2.toggleIsActive();
}
ToggleIsActive should just be the opposite of what it once was. Also note that switchPlayer only calls toggle with no specific logic.
You can achieve this by removing the if/else from the switchPlayer() implementation:
function switchPlayer(){
player1.toggleIsActive();
player2.toggleIsActive();
}
Also, consider simplifying your toggleIsActive() method on the Player prototype like so:
Player.prototype.toggleIsActive = function(){
this.isActive = !this.isActive;
}
Here's a full example:
var Player = function(score, isActive){
this.score = score;
this.isActive = isActive;
}
Player.prototype.toggleIsActive = function(){
this.isActive = !this.isActive;
}
function switchPlayer(){
player1.toggleIsActive();
player2.toggleIsActive();
}
var player1 = new Player("0", true);
var player2 = new Player("0", false);
console.log('player1.isActive', player1.isActive)
console.log('player2.isActive', player2.isActive)
console.log('----------------')
switchPlayer();
console.log('player1.isActive', player1.isActive)
console.log('player2.isActive', player2.isActive)
console.log('----------------')
switchPlayer();
console.log('player1.isActive', player1.isActive)
console.log('player2.isActive', player2.isActive)
console.log('----------------')
let player1 = {};
let player2 = {};
player1.isActive = false;
player2.isActive = true;
function toggle () {
player1.isActive = !player1.isActive;
player2.isActive = !player2.isActive;
console.log('player1', player1.isActive, 'player2', player2.isActive);
}
<button onclick="toggle()">Toggle</button>

JavaScript - Set function to return all items based on 1 or 2 selected tags (NO jQUERY)

I have some JavaScrip that is meant to check if there are any media tags selected or industry tags selected--this is so the portfolio items can be sorted and displayed accordingly in the browser.
What I have almost works 100%, but I can't figure out how to make it so that if only a media tag is selected or if only an industry tag is selected, the portfolio items should still be sorted accordingly. Currently, you have to select a media tag AND an industry tag, but I'd like users to be able to search using just a media tag OR just an industry tag.
Here is what I want to accomplish: If only a media tag is selected, then get all portfolio pieces that are associated with that media tag. If only an industry tag is selected, get all portfolio items that are associated with that industry tag. If a media tag AND industry tag are selected at the same time, get all portfolio items that are associated with BOTH.
Vanilla JS isn't my strong point so forgive me if this is a dumb question, but this has had me stumped for hours now.
No jQuery answers, please, as this whole page's functionality is built using JavaScript.
Here is the function:
var update = function () {
closeDrawer();
// update ui to reflect tag changes
// get our list of items to display
var itemsToDisplay = [];
var currentMediaTag = controlsContainer.querySelector('.media.selected');
var currentIndustryTag = controlsContainer.querySelector('.industry.selected');
if (currentMediaTag != "" && currentMediaTag != null) {
selectedMediaFilter = currentMediaTag.innerHTML;
}
if (currentIndustryTag != "" && currentIndustryTag != null) {
selectedIndustryFilter = currentIndustryTag.innerHTML;
}
if (selectedMediaFilter == "" && selectedIndustryFilter == "") {
itemsToDisplay = portfolioItems.filter(function (item) {
return item.preferred;
});
} else {
itemsToDisplay = portfolioItems.filter(function (item) {
var mediaTags = item.media_tags,
industryTags = item.industry_tags;
if(industryTags.indexOf(selectedIndustryFilter) < 0){
return false;
}
else if(mediaTags.indexOf(selectedMediaFilter) < 0){
return false;
}
else{
return true;
}
});
}
renderItems(itemsToDisplay);
}
Not entirely sure it's necessary but just in case, here is the complete JS file that handles the portfolio page:
(function ($) {
document.addEventListener("DOMContentLoaded", function (event) {
// for portfolio interaction
var portfolioGrid = (function () {
var gridSize = undefined,
parentContainer = document.querySelector('.portfolio-item-container');
containers = parentContainer.querySelectorAll('.view'),
drawer = parentContainer.querySelector('.drawer'),
bannerContainer = drawer.querySelector('.banner-container'),
thumbsContainer = drawer.querySelector('.thumbs-container'),
descriptionContainer = drawer.querySelector('.client-description'),
clientNameContainer = drawer.querySelector('.client-name'),
controlsContainer = document.querySelector('.portfolio-controls-container'),
selectedMediaFilter = "", selectedIndustryFilter = "";
var setGridSize = function () {
var windowSize = window.innerWidth,
previousGridSize = gridSize;
if (windowSize > 1800) {
gridSize = 5;
} else if (windowSize > 900) {
gridSize = 4;
} else if (windowSize > 600 && windowSize <= 900) {
gridSize = 3;
} else {
gridSize = 2;
}
if (previousGridSize != gridSize) {
closeDrawer();
}
};
var attachResize = function () {
window.onresize = function () {
setGridSize();
};
};
var getRowClicked = function (boxNumber) {
return Math.ceil(boxNumber / gridSize);
};
var getLeftSibling = function (row) {
var cI = row * gridSize;
return containers[cI >= containers.length ? containers.length - 1 : cI];
};
var openDrawer = function () {
drawer.className = 'drawer';
scrollToBanner();
};
var scrollToBanner = function () {
var mainContainer = document.querySelector('#main-container'),
mainBounding = mainContainer.getBoundingClientRect(),
scrollY = (drawer.offsetTop - mainBounding.bottom) - 10,
currentTop = document.body.getBoundingClientRect().top;
animate(document.body, "scrollTop", "", document.body.scrollTop, scrollY, 200, true);
};
var animate = function (elem, style, unit, from, to, time, prop) {
if (!elem) return;
var start = new Date().getTime(),
timer = setInterval(function () {
var step = Math.min(1, (new Date().getTime() - start) / time);
if (prop) {
elem[style] = (from + step * (to - from)) + unit;
} else {
elem.style[style] = (from + step * (to - from)) + unit;
}
if (step == 1) clearInterval(timer);
}, 25);
elem.style[style] = from + unit;
}
var closeDrawer = function () {
drawer.className = 'drawer hidden';
};
var cleanDrawer = function () {
bannerContainer.innerHTML = "";
clientNameContainer.innerHTML = "";
descriptionContainer.innerHTML = "";
thumbsContainer.innerHTML = "";
};
var resetThumbs = function () {
Array.prototype.forEach.call(thumbsContainer.querySelectorAll('.thumb'), function (t) {
t.className = "thumb";
});
};
var handleBannerItem = function (item) {
bannerContainer.innerHTML = "";
if (item.youtube) {
var videoContainer = document.createElement('div'),
iframe = document.createElement('iframe');
videoContainer.className = "videowrapper";
iframe.className = "youtube-video";
iframe.src = "https://youtube.com/embed/" + item.youtube;
videoContainer.appendChild(iframe);
bannerContainer.appendChild(videoContainer);
} else if (item.soundcloud) {
var iframe = document.createElement('iframe');
iframe.src = item.soundcloud;
iframe.className = "soundcloud-embed";
bannerContainer.appendChild(iframe);
} else if (item.banner) {
var bannerImage = document.createElement('img');
bannerImage.src = item.banner;
bannerContainer.appendChild(bannerImage);
}
};
var attachClick = function () {
Array.prototype.forEach.call(containers, function (n, i) {
n.querySelector('a.info').addEventListener('click', function (e) {
e.preventDefault();
});
n.addEventListener('click', function (e) {
var boxNumber = i + 1,
row = getRowClicked(boxNumber);
var containerIndex = row * gridSize;
if (containerIndex >= containers.length) {
// we're inserting drawer at the end
parentContainer.appendChild(drawer);
} else {
// we're inserting drawer in the middle somewhere
var leftSiblingNode = getLeftSibling(row);
leftSiblingNode.parentNode.insertBefore(drawer, leftSiblingNode);
}
// populate
cleanDrawer();
var mediaFilterSelected = document.querySelector('.media-tags .tag-container .selected');
var selectedFilters = "";
if (mediaFilterSelected != "" && mediaFilterSelected != null) {
selectedFilters = mediaFilterSelected.innerHTML;
}
var portfolioItemName = '';
var selectedID = this.getAttribute('data-portfolio-item-id');
var data = portfolioItems.filter(function (item) {
portfolioItemName = item.name;
return item.id === selectedID;
})[0];
clientNameContainer.innerHTML = data.name;
descriptionContainer.innerHTML = data.description;
var childItems = data.child_items;
//We will group the child items by media tag and target the unique instance from each group to get the right main banner
Array.prototype.groupBy = function (prop) {
return this.reduce(function (groups, item) {
var val = item[prop];
groups[val] = groups[val] || [];
groups[val].push(item);
return groups;
}, {});
}
var byTag = childItems.groupBy('media_tags');
if (childItems.length > 0) {
handleBannerItem(childItems[0]);
var byTagValues = Object.values(byTag);
byTagValues.forEach(function (tagValue) {
for (var t = 0; t < tagValue.length; t++) {
if (tagValue[t].media_tags == selectedFilters) {
handleBannerItem(tagValue[0]);
}
}
});
childItems.forEach(function (item, i) {
var img = document.createElement('img'),
container = document.createElement('div'),
label = document.createElement('p');
container.appendChild(img);
var mediaTags = item.media_tags;
container.className = "thumb";
label.className = "childLabelInactive thumbLbl";
thumbsContainer.appendChild(container);
if (selectedFilters.length > 0 && mediaTags.length > 0) {
for (var x = 0; x < mediaTags.length; x++) {
if (mediaTags[x] == selectedFilters) {
container.className = "thumb active";
label.className = "childLabel thumbLbl";
}
}
}
else {
container.className = i == 0 ? "thumb active" : "thumb";
}
img.src = item.thumb;
if (item.media_tags != 0 && item.media_tags != null) {
childMediaTags = item.media_tags;
childMediaTags.forEach(function (cMTag) {
varLabelTxt = document.createTextNode(cMTag);
container.appendChild(label);
label.appendChild(varLabelTxt);
});
}
img.addEventListener('click', function (e) {
scrollToBanner();
resetThumbs();
handleBannerItem(item);
container.className = "thumb active";
});
});
}
openDrawer();
});
});
};
var preloadImages = function () {
portfolioItems.forEach(function (item) {
var childItems = item.child_items;
childItems.forEach(function (child) {
(new Image()).src = child.banner;
(new Image()).src = child.thumb;
});
});
};
//////////////////////////////////// UPDATE FUNCTION /////////////////////////////////////
var update = function () {
closeDrawer();
// update ui to reflect tag changes
// get our list of items to display
var itemsToDisplay = [];
var currentMediaTag = controlsContainer.querySelector('.media.selected');
var currentIndustryTag = controlsContainer.querySelector('.industry.selected');
if (currentMediaTag != "" && currentMediaTag != null) {
selectedMediaFilter = currentMediaTag.innerHTML;
}
if (currentIndustryTag != "" && currentIndustryTag != null) {
selectedIndustryFilter = currentIndustryTag.innerHTML;
}
if (selectedMediaFilter == "" && selectedIndustryFilter == "") {
itemsToDisplay = portfolioItems.filter(function (item) {
return item.preferred;
});
} else {
itemsToDisplay = portfolioItems.filter(function (item) {
var mediaTags = item.media_tags,
industryTags = item.industry_tags;
if (industryTags.indexOf(selectedIndustryFilter) < 0) {
return false;
}
else if (mediaTags.indexOf(selectedMediaFilter) < 0) {
return false;
}
else {
return true;
}
});
}
renderItems(itemsToDisplay);
}
//////////////////////////////////// RENDERITEMS FUNCTION /////////////////////////////////////
var renderItems = function (items) {
var children = parentContainer.querySelectorAll('.view');
Array.prototype.forEach.call(children, function (child) {
// remove all event listeners then remove child
parentContainer.removeChild(child);
});
items.forEach(function (item) {
var container = document.createElement('div'),
thumb = document.createElement('img'),
mask = document.createElement('div'),
title = document.createElement('h6'),
excerpt = document.createElement('p'),
link = document.createElement('a');
container.className = "view view-tenth";
container.setAttribute('data-portfolio-item-id', item.id);
thumb.src = item.thumb;
mask.className = "mask";
title.innerHTML = item.name;
excerpt.innerHTML = item.excerpt;
link.href = "#";
link.className = "info";
link.innerHTML = "View Work";
container.appendChild(thumb);
container.appendChild(mask);
mask.appendChild(title);
mask.appendChild(excerpt);
mask.appendChild(link);
parentContainer.insertBefore(container, drawer);
});
containers = parentContainer.querySelectorAll('.view');
attachClick();
};
var filterHandler = function (linkNode, tagType) {
var prevSelection = document.querySelector("." + tagType + '.selected');
if (prevSelection != "" && prevSelection != null) {
prevSelection.className = tagType + ' tag';
}
linkNode.className = tagType + ' tag selected';
update();
};
var clearFilters = function (nodeList, filterType) {
Array.prototype.forEach.call(nodeList, function (node) {
node.className = filterType + " tag";
console.log("Clear filters function called");
});
}
var attachFilters = function () {
var mediaFilters = controlsContainer.querySelectorAll('.tag.media'),
industryFilters = controlsContainer.querySelectorAll('.tag.industry'),
filterToggle = controlsContainer.querySelectorAll('.filter-toggle');
// resets
controlsContainer.querySelector('.media-tags .reset')
.addEventListener('click',
function (e) {
e.preventDefault();
selectedMediaFilter = "";
clearFilters(controlsContainer.querySelectorAll('.media-tags a.tag'), "media");
update();
}
);
controlsContainer.querySelector('.industry-tags .reset')
.addEventListener('click',
function (e) {
e.preventDefault();
selectedIndustryFilter = "";
clearFilters(controlsContainer.querySelectorAll('.industry-tags a.tag'), "industry");
update();
}
);
Array.prototype.forEach.call(filterToggle, function (toggle) {
toggle.addEventListener('click', function (e) {
if (controlsContainer.className.indexOf('open') < 0) {
controlsContainer.className += ' open';
} else {
controlsContainer.className = controlsContainer.className.replace('open', '');
}
});
});
//Attaches a click event to each media tag "button"
Array.prototype.forEach.call(mediaFilters, function (filter) {
filter.addEventListener('click', function (e) {
e.preventDefault();
// var selectedMediaFilter = controlsContainer.querySelector('.media.selected');
//console.log("Media tag: " +this.innerHTML); *THIS WORKS*
filterHandler(this, "media");
});
});
Array.prototype.forEach.call(industryFilters, function (filter) {
filter.addEventListener('click', function (e) {
e.preventDefault();
// var selectedIndustryFilter = this.querySelector('.industry.selected');
// console.log("Industry tag: " +this.innerHTML); *THIS WORKS*
filterHandler(this, "industry");
});
});
};
return {
init: function () {
setGridSize();
attachResize();
attachClick();
preloadImages();
// portfolio page
if (controlsContainer) {
attachFilters();
}
}
};
})();
portfolioGrid.init();
});
}());
$ = jQuery.noConflict();
if(industryTags.indexOf(selectedIndustryFilter) < 0){
return false;
}
else if(mediaTags.indexOf(selectedMediaFilter) < 0){
return false;
}
That part is giving you headaches. Whenever no industry tag or media tag is selected this will exit the function.
Change to:
if(industryTags.indexOf(selectedIndustryFilter) < 0 && mediaTags.indexOf(selectedMediaFilter) < 0){
return false;
}
Now it will test if at least one tag is selected. If so then render items.
I made a change just to experiment with an idea, and this setup works:
if((selectedIndustryFilter !="" && industryTags.indexOf(selectedIndustryFilter) < 0) || (selectedMediaFilter !="" && mediaTags.indexOf(selectedMediaFilter) < 0)){
return false;
}
return true;
Not sure if it's the best solution ever but it seems to work and I'm not going to complain.

HTML button that's submitting an empty field even though it shouldn't be

Here's the HTML button I'm working with:
<b>Other: </b><input type="number" id="AmntValue" data-target-element-id="SubmitAmnt" data-target-parameter="Amnt" onchange="setValueOnTarget(this);' +/* ' enableButton(SubmitAmnt);' */+ '">
<button class="button2" id="SubmitAmnt" type="button" data-redirect-src="https://hub.deltasigmapi.org/donations/donations.aspx?appealid=1989&NumberOfPaymentsDisplay=0&GiftRecurrenceDisplay=0&GiftRecurrence=onetime&GiftAmount=" onclick="disableButton(this); addValueToQueryString(this); redirectPage(this);">Continue To Payment</button>
When someone hits the button but the "Other" text field is blank, it's supposed to not redirect and instead show an error message. Right now the error message displays, but only for a quick moment before it redirects anyway.
Here is my complete JavaScript code:
function setValueOnTarget(sourceElem) {
var targetId = sourceElem.getAttribute('data-target-element-id');
if (targetId) {
var targetElem = document.getElementById(targetId);
if (targetElem) {
var valueToSet;
var parameterToSet;
if (sourceElem.nodeName.toUpperCase() == 'SELECT') {
valueToSet = sourceElem.options[sourceElem.selectedIndex].value;
}
if (sourceElem.nodeName.toUpperCase() == 'INPUT') {
if (sourceElem.type.toUpperCase() == 'NUMBER' || sourceElem.type.toUpperCase() == 'TEXT') {
valueToSet = sourceElem.value;
}
}
targetElem.setAttribute('data-value-set-by-other-element', valueToSet);
parameterToSet = sourceElem.getAttribute('data-target-parameter');
targetElem.setAttribute('data-target-parameter', parameterToSet);
EnableButton(targetElem)
}
}
}
function disableButton(btn) {
btn.disabled = true;
}
function EnableButton(btn) {
btn.disabled = false;
}
function addValueToQueryString(elem) {
var src = elem.getAttribute('data-redirect-src');
var newValue = elem.getAttribute('data-value-set-by-other-element');
var parameter = elem.getAttribute('data-target-parameter');
if (newValue && parameter) {
if (src && newValue && parameter) {
var newSrc;
newSrc = src + newValue;
elem.setAttribute('data-redirect-src', newSrc);
} else {
displayError('Could not find the URL to redirect to');
}
} else {
displayError('No value or parameter has been set. Please set a proper value.');
}
}
function redirectPage(elem) {
var src = elem.getAttribute('data-redirect-src');
window.location = src;
}
function displayError(message) {
var userMessage = document.getElementById('userMessage');
userMessage.innerHTML = message;
userMessage.style.backgroundColor = 'red';
userMessage.style.color = 'white';
userMessage.style.display = 'block';
}
function displaySuccess(message) {
var userMessage = document.getElementById('userMessage1');
userMessage.innerHTML = message;
userMessage.style.backgroundColor = 'green';
userMessage.style.color = 'white';
userMessage.style.display = 'block';
}
I'm not sure if something's wrong with the code I put in the button or in the JavaScript.
Disable button by default
The button should be disabled by default, and should only be enabled when the expected input value is detected. It appears you already have a mechanism for this in your example, but you have some impediments to overcome first:
button should be disabled by default. Do this in the HTML:<button disabled …>Continue To Payment</button>
input's onchange handler should just call setValueOnTarget(), because this function already calls EnableButton(). In the HTML:<input onchange="setValueOnTarget(this);" … >
Remove the call to redirectPage() from the button's onclick handler and move it into addValueToQueryString() after you have assigned a value to newSrc.
Add a call to EnableButton() after you call displayError() in cases where you want to allow the user to modify the input and try again.
For example:
function setValueOnTarget(sourceElem) {
var targetId = sourceElem.getAttribute('data-target-element-id');
if (targetId) {
var targetElem = document.getElementById(targetId);
console.log(targetElem);
if (targetElem) {
var valueToSet;
var parameterToSet;
if (sourceElem.nodeName.toUpperCase() == 'SELECT') {
valueToSet = sourceElem.options[sourceElem.selectedIndex].value;
}
if (sourceElem.nodeName.toUpperCase() == 'INPUT') {
if (sourceElem.type.toUpperCase() == 'NUMBER' || sourceElem.type.toUpperCase() == 'TEXT') {
valueToSet = sourceElem.value;
}
}
targetElem.setAttribute('data-value-set-by-other-element', valueToSet);
parameterToSet = sourceElem.getAttribute('data-target-parameter');
targetElem.setAttribute('data-target-parameter', parameterToSet);
EnableButton(targetElem);
}
}
}
function disableButton(btn) {
btn.disabled = true;
}
function EnableButton(btn) {
btn.disabled = false;
}
function addValueToQueryString(elem) {
var src = elem.getAttribute('data-redirect-src');
var newValue = elem.getAttribute('data-value-set-by-other-element');
var parameter = elem.getAttribute('data-target-parameter');
if (newValue && parameter) {
if (src && newValue && parameter) {
var newSrc;
newSrc = src + newValue;
elem.setAttribute('data-redirect-src', newSrc);
redirectPage(elem);
} else {
displayError('Could not find the URL to redirect to');
}
} else {
displayError('No value or parameter has been set. Please set a proper value.');
EnableButton(elem);
}
}
function redirectPage(elem) {
var src = elem.getAttribute('data-redirect-src');
window.location = src;
}
function displayError(message) {
var userMessage = document.getElementById('userMessage');
userMessage.innerHTML = message;
userMessage.style.backgroundColor = 'red';
userMessage.style.color = 'white';
userMessage.style.display = 'block';
}
<b>Other: </b>
<input
type="number"
id="AmntValue"
data-target-element-id="SubmitAmnt"
data-target-parameter="Amnt"
onchange="setValueOnTarget(this);">
<button
disabled
class="button2"
id="SubmitAmnt"
type="button"
data-redirect-src="https://hub.deltasigmapi.org/donations/donations.aspx?appealid=1989&NumberOfPaymentsDisplay=0&GiftRecurrenceDisplay=0&GiftRecurrence=onetime&GiftAmount="
onclick="disableButton(this); addValueToQueryString(this);">Continue To Payment</button>
<div id="userMessage"></div>

Categories

Resources