Updating Auto Suggestions on Space Key Press In Vuejs - javascript

Hie i am Implementing Auto suggestion on search input
it has 2 feature where it suggests the next word as well as the approxiamte sentence/Question.
i have implemented most of the things..
i am stuck in the second half implementation where the questions which i am displaying in the dropdown will have to be updated when user press the Space key..
<template>
<div>
<div class="RNNXgb">
<div class="SDkEP">
<div class="iblpc" v-if="isOpen">
<div class="CcAdNb">
<span class="QCzoEc z1asCe MZy1Rb">
<i class="ti-search" />
</span>
</div>
</div>
<div class="a4bIc">
<div class="pR49Ae gsfi"></div>
<input
class="gLFyf gsfi"
maxlength="2048"
type="text"
aria-autocomplete="both"
aria-haspopup="false"
autocapitalize="off"
autocomplete="off"
autocorrect="off"
role="combobox"
spellcheck="false"
aria-label="Search"
#input="onChange"
v-model="search"
#keydown.down="onArrowDown"
#keydown.up="onArrowUp"
#keydown.enter="onEnter"
#keyup.space="getData"
placeholder="Search your Query"
/>
</div>
<div class="dRYYxd">
<div class="BKRPef" v-if="isOpen" aria-label="Clear" role="button">
<span
class="ExCKkf z1asCe rzyADb"
role="button"
tabindex="0"
#click="closeDropdown"
>
<i class="ti-close"></i
></span>
<span class="ACRAdd"></span>
</div>
<div
class="XDyW0e"
aria-label="Search by voice"
role="button"
tabindex="0"
>
<i class="ti-microphone"></i>
</div>
</div>
</div>
<button class="Tg7LZd" type="submit" aria-label="Search Your Query">
<div class="zgAlFc">
<span class="z1asCe MZy1Rb">
<i class="ti-search icon" />
</span>
</div>
</button>
<div class="UUbT9" style="margin-top:40px">
<div class="aajZCb">
<!-- <div class="xtSCL"></div> -->
<div style="height:150px;overflow-y:scroll" v-if="isOpen">
<ul id="autocomplete-results" class="G43f7e">
<li
v-for="(result, i) in articles"
:key="i"
#click="setResult(result)"
class="sbct"
:class="{ 'is-active': i === arrowCounter }"
>
<div class="eIPGRd">
<div class="sbic sb43">
<i class="ti-search" />
</div>
<div class="pcTkSc">
<span class="wM6W7d WggQGd">{{ result }}</span>
</div>
</div>
</li>
</ul>
</div>
<div
v-show="isOpen"
style="height:150px;overflow-y:scroll;border-top: 1px solid #e8eaed;"
>
<ul id="autocomplete-results" class="G43f7e">
<li
v-for="(result, i) in results"
:key="i"
#click="setResult(result)"
class="sbct"
:class="{ 'is-active': i === arrowCounter }"
>
<div class="eIPGRd">
<div class="sbic sb43">
<i class="ti-search" />
</div>
<div class="pcTkSc">
<span class="wM6W7d WggQGd">{{ result }}</span>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "SearchAutocomplete",
props: {
// items: {
// type: Array,
// required: false,
// default: () => [],
// },
isAsync: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
articles: [
"the",
"be",
"to",
"of",
"and",
"a",
"in",
"that",
"have",
"I",
"it",
"for",
"not",
"on",
"with",
"he",
"as",
"you",
"do",
"at",
"this",
"but",
"his",
"by",
"from",
"they",
"we",
"say",
"her",
"she",
"or",
"an",
"will",
"my",
"one",
"all",
"would",
"there",
"their",
"what",
"so",
"up",
"out",
"if",
"about",
"who",
"get",
"which",
"go",
"me",
"when",
"make",
"can",
"like",
"time",
"no",
"just",
"him",
"know",
"take",
"people",
"into",
"year",
"your",
"good",
"some",
"could",
"them",
"see",
"other",
"than",
"then",
"now",
"look",
"only",
"come",
"its",
"over",
"think",
"also",
"back",
"after",
"use",
"two",
"how",
"our",
"work",
"first",
"well",
"way",
"even",
"new",
"want",
"because",
"any",
"these",
"give",
"day",
"most",
"us",
],
items: [
"Apple is a fruit",
"Banana",
"Orange",
"Mango not includd",
"Pear",
"Peach not",
"Grape",
"Tangerine not",
"Pineapple",
],
isOpen: false,
results: [],
search: "",
isLoading: false,
arrowCounter: -1,
};
},
watch: {
items(value, oldValue) {
if (value.length !== oldValue.length) {
this.results = value;
this.isLoading = false;
}
},
},
mounted() {
document.addEventListener("click", this.handleClickOutside);
},
destroyed() {
document.removeEventListener("click", this.handleClickOutside);
},
methods: {
setResult(result) {
this.search = result;
this.isOpen = false;
alert(result);
},
filterResults() {
this.results = this.items.filter((item) => {
return item.toLowerCase().indexOf(this.search.toLowerCase()) > -1;
});
},
onChange() {
this.$emit("input", this.search);
this.filterResults();
this.isOpen = true;
},
handleClickOutside(event) {
if (!this.$el.contains(event.target)) {
this.isOpen = false;
this.arrowCounter = -1;
}
},
closeDropdown() {
this.search = "";
this.isOpen = false;
},
onArrowDown() {
if (this.arrowCounter < this.results.length) {
this.arrowCounter = this.arrowCounter + 1;
}
},
onArrowUp() {
if (this.arrowCounter > 0) {
this.arrowCounter = this.arrowCounter - 1;
}
},
onEnter() {
this.search = this.results[this.arrowCounter];
this.isOpen = false;
this.arrowCounter = -1;
alert(this.search);
},
onSearch() {
alert(this.search);
},
getData(){
alert("space pressed")
}
},
};
</script>
<style>
.sb43 {
background-size: 20px;
min-height: 20px;
min-width: 20px;
height: 20px;
width: 20px;
}
.sbic {
display: flex;
align-items: center;
margin-right: 14px;
}
.WggQGd {
color: #52188c;
}
.wM6W7d {
display: flex;
font-size: 16px;
color: #212121;
flex: auto;
align-items: center;
word-break: break-word;
padding-right: 8px;
}
.pcTkSc {
display: flex;
flex: auto;
flex-direction: column;
min-width: 0;
max-height: none;
padding: 6px 0;
}
.eIPGRd {
flex: auto;
display: flex;
align-items: center;
margin: 0 20px 0 14px;
}
.sbct {
display: flex;
align-items: center;
min-width: 0;
max-height: none;
padding: 0;
}
.sbct:hover {
background: #eee;
}
.G43f7e {
display: flex;
flex-direction: column;
min-width: 0;
padding: 0;
}
.xtSCL {
border-top: 1px solid #e8eaed;
margin: 0 14px;
padding-bottom: 4px;
}
.aajZCb {
background: #fff;
box-shadow: 0 9px 8px -3px rgb(64 60 67 / 24%),
8px 0 8px -7px rgb(64 60 67 / 24%), -8px 0 8px -7px rgb(64 60 67 / 24%);
display: flex;
flex-direction: column;
list-style-type: none;
margin: 0;
padding: 0;
border: 0;
border-radius: 0 0 24px 24px;
padding-bottom: 4px;
overflow: hidden;
}
.UUbT9 {
position: absolute;
width: 690px;
text-align: left;
margin-top: -1px;
z-index: 989;
cursor: default;
-webkit-user-select: none;
}
.gLFyf {
background-color: transparent;
border: none;
margin: 0;
padding: 0;
color: rgba(0, 0, 0, 0.87);
word-wrap: break-word;
outline: none;
display: flex;
flex: 100%;
-webkit-tap-highlight-color: transparent;
margin-top: -37px;
height: 34px;
font-size: 16px;
}
.gLFyf {
height: 39px !important;
line-height: 39px;
margin-top: -42px;
}
.gLFyf.i4ySpb {
display: block;
}
.gsfi,
.lst {
font: 16px arial, sans-serif;
color: rgba(0, 0, 0, 0.87);
line-height: 34px;
height: 34 px !important;
}
.pR49Ae {
color: transparent;
flex: 100%;
white-space: pre;
height: 39px !important;
line-height: 39px;
}
.a4bIc {
display: flex;
flex: 1;
flex-wrap: wrap;
}
.XDyW0e {
flex: 1 0 auto;
display: flex;
cursor: pointer;
align-items: center;
border: 0;
background: transparent;
outline: none;
padding: 0 8px;
width: 24px;
line-height: 44px;
}
.ACRAdd {
border-left: 1px solid #dfe1e5;
height: 65%;
}
.ExCKkf {
margin-right: 12px;
}
.ExCKkf {
height: 100%;
color: #70757a;
vertical-align: middle;
outline: none;
}
.BKRPef {
padding-right: 4px;
}
.BKRPef {
flex: 1 0 auto;
display: flex;
cursor: pointer;
align-items: center;
border: 0;
background: transparent;
outline: none;
padding: 0 8px;
line-height: 44px;
}
.dRYYxd {
display: flex;
flex: 0 0 auto;
margin-top: -5px;
align-items: stretch;
flex-direction: row;
}
.QCzoEc {
color: #9aa0a6;
height: 20px;
width: 20px;
}
.CcAdNb {
margin: auto;
}
.iblpc {
display: flex;
align-items: center;
padding-right: 6px;
margin-top: -7px;
}
.RNNXgb {
background: #fff;
display: flex;
border: 1px solid transparent;
box-shadow: 0 2px 5px 1px rgb(64 60 67 / 16%);
height: 39px;
width: 690px;
border-radius: 24px;
z-index: 3;
height: 44px;
margin: 0 auto;
}
.SDkEP {
flex: 1;
display: flex;
padding: 5px 4px 0 16px;
padding-left: 14px;
}
.Tg7LZd {
height: 44px;
width: 44px;
background: transparent;
border: none;
cursor: pointer;
flex: 0 0 auto;
padding: 0;
flex: 0 0 auto;
padding-right: 13px;
}
.zgAlFc {
background: none;
color: #4285f4;
height: 24px;
width: 24px;
margin: auto;
}
.z1asCe {
display: inline-block;
fill: currentColor;
height: 24px;
line-height: 24px;
position: relative;
width: 24px;
}
.z1asCe i {
display: block;
height: 100%;
width: 100%;
}
::-webkit-scrollbar {
background: silver;
width: 4px;
height: 4px;
margin-top: 24px;
}
:hover::-webkit-scrollbar {
background: silver;
/* color:pink; */
width: 8px;
height: 8px;
border-radius: 0.002px;
}
</style>
this is just a dummy/demo version it has different implementation but the logic and flow remains same. can anyone of you help me in this

Related

Why does the update of this computed property fail in my Vue 3 app?

I am working on an audio player with Vue 3 and the Napster API.
Project details
I have made the vinyl spin with the help of a CSS keyframes-based animation and the isSpinning computed property.
I want the vinyl to stop spinning once the end of the current track is reached, which is why isSpinning has this "formula":
isSpinning() {
return this.isPlaying && !this.player.ended;
}
const musicApp = {
data() {
return {
player: new Audio(),
trackCount: 0,
tracks: [],
muted: false,
isPlaying: false
};
},
methods: {
async getTracks() {
try {
const response = await axios
.get(
"https://api.napster.com/v2.1/tracks/top?apikey=ZTk2YjY4MjMtMDAzYy00MTg4LWE2MjYtZDIzNjJmMmM0YTdm"
)
.catch((error) => {
console.log(error);
});
this.tracks = response;
this.tracks = response.data.tracks;
} catch (error) {
console.log(error);
}
},
nextTrack() {
if (this.trackCount < this.tracks.length - 1) {
this.trackCount++;
this.setPlayerSource();
this.playPause();
}
},
prevTrack() {
if (this.trackCount >= 1) {
this.trackCount--;
this.setPlayerSource();
this.playPause();
}
},
setPlayerSource() {
this.player.src = this.tracks[this.trackCount].previewURL;
},
playPause() {
if (this.player.paused) {
this.isPlaying = true;
this.player.play();
} else {
this.isPlaying = false;
this.player.pause();
}
},
toggleMute() {
this.player.muted = !this.player.muted;
this.muted = this.player.muted;
}
},
async created() {
await this.getTracks();
this.setPlayerSource();
},
computed: {
isSpinning() {
return this.isPlaying && !this.player.ended;
}
}
};
Vue.createApp(musicApp).mount("#audioPlayer");
html,
body {
margin: 0;
padding: 0;
font-size: 16px;
}
body * {
box-sizing: border-box;
font-family: "Montserrat", sans-serif;
}
#-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
}
}
#keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.player-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #2998ff;
background-image: linear-gradient(62deg, #2998ff 0%, #5966eb 100%);
}
#audioPlayer {
width: 300px;
height: 300px;
border-radius: 8px;
position: relative;
overflow: hidden;
background-color: #00ca81;
background-image: linear-gradient(160deg, #00ca81 0%, #ffffff 100%);
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
display: flex;
flex-direction: column;
align-items: center;
}
.volume {
color: #ff0057;
opacity: 0.9;
display: inline-block;
width: 20px;
font-size: 20px;
position: absolute;
top: 5px;
right: 6px;
cursor: pointer;
}
.album {
width: 100%;
flex: 1;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
.album-items {
padding: 0 10px;
text-align: center;
}
.cover {
width: 150px;
height: 150px;
margin: auto;
box-shadow: 0px 5px 12px 0px rgba(0, 0, 0, 0.17);
border-radius: 50%;
background: url("https://w7.pngwing.com/pngs/710/955/png-transparent-vinyl-record-artwork-phonograph-record-compact-disc-lp-record-disc-jockey-symbol-miscellaneous-classical-music-sound.png") center top transparent;
background-size: cover;
}
.cover.spinning {
webkit-animation: spin 6s linear infinite;
/* Safari */
animation: spin 6s linear infinite;
}
.info {
width: 100%;
padding-top: 5px;
color: #000;
opacity: 0.85;
}
.info h1 {
font-size: 11px;
margin: 5px 0 0 0;
}
.info h2 {
font-size: 10px;
margin: 3px 0 0 0;
}
.to-bottom {
width: 100%;
margin-top: auto;
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.track {
background-color: #ff0057;
opacity: 0.9;
height: 3px;
width: 100%;
}
.controls {
width: 150px;
display: flex;
height: 60px;
justify-content: space-between;
align-items: center;
}
.controls .navigate {
display: flex;
box-shadow: 1px 2px 7px 2px rgba(0, 0, 0, 0.09);
width: 33px;
height: 33px;
line-height: 1;
color: #ff0057;
cursor: pointer;
background: #fff;
opacity: 0.9;
border-radius: 50%;
text-align: center;
justify-content: center;
align-items: center;
}
.controls .navigate.disabled {
pointer-events: none;
color: #606060;
background-color: #f7f7f7;
}
.controls .navigate.navigate-play {
width: 38px;
height: 38px;
}
.navigate-play .fa-play {
margin-left: 3px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<script src="https://unpkg.com/axios#0.22.0/dist/axios.min.js"></script>
<script src="https://unpkg.com/vue#next"></script>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght#300;500&display=swap" rel="stylesheet">
<div class="player-container">
<div id="audioPlayer">
<span class="volume" #click="toggleMute">
<i v-show="!muted" class="fa fa-volume-up"></i>
<i v-show="muted" class="fa fa-volume-off"></i>
</span>
<div class="album">
<div class="album-items">
<div class="cover" :class="{'spinning' : isSpinning}"></div>
<div class="info">
<h1>{{tracks[trackCount].name}}</h1>
<h2>{{tracks[trackCount].artistName}}</h2>
</div>
</div>
</div>
<div class="to-bottom">
<div class="track"></div>
<div class="controls">
<div class="navigate navigate-prev" :class="{'disabled' : trackCount == 0}" #click="prevTrack">
<i class="fa fa-step-backward"></i>
</div>
<div class="navigate navigate-play" #click="playPause">
<i v-show="!isPlaying" class="fa fa-play"></i>
<i v-show="isPlaying" class="fa fa-pause"></i>
</div>
<div class="navigate navigate-next" :class="{'disabled' : trackCount == tracks.length - 1}" #click="nextTrack">
<i class="fa fa-step-forward"></i>
</div>
</div>
</div>
</div>
</div>
The problem
But, to my surprise, the app is unaware of the fact that the value of this.player.ended has changed (or is supposed to).
What am I doing wrong?
An Audio object will not function in the same manner as normal JavaScript objects in Vue, as the internals that Vue uses to abstract away state change observation will not be maintained when the Audio object changes state. In other words, the thing that allows Vue to detect the Audio object switching from ended === false to ended === true won't work, preventing Vue from knowing that the component needs to be updated.
If you wish to observe a change in ended state, then you'll want to add a custom event listener to the object in your created hook to toggle the spinning state and simply remove the ended check:
const musicApp = {
data() {
return {
player: new Audio(),
trackCount: 0,
tracks: [],
muted: false,
isPlaying: false
};
},
methods: {
async getTracks() {
try {
const response = await axios
.get(
"https://api.napster.com/v2.1/tracks/top?apikey=ZTk2YjY4MjMtMDAzYy00MTg4LWE2MjYtZDIzNjJmMmM0YTdm"
)
.catch((error) => {
console.log(error);
});
this.tracks = response;
this.tracks = response.data.tracks;
} catch (error) {
console.log(error);
}
},
nextTrack() {
if (this.trackCount < this.tracks.length - 1) {
this.trackCount++;
this.setPlayerSource();
this.playPause();
}
},
prevTrack() {
if (this.trackCount >= 1) {
this.trackCount--;
this.setPlayerSource();
this.playPause();
}
},
setPlayerSource() {
this.player.src = this.tracks[this.trackCount].previewURL;
},
playPause() {
if (this.player.paused) {
this.isPlaying = true;
this.player.play();
} else {
this.isPlaying = false;
this.player.pause();
}
},
toggleMute() {
this.player.muted = !this.player.muted;
this.muted = this.player.muted;
}
},
async created() {
await this.getTracks();
this.setPlayerSource();
this.player.addEventListener('ended', () => {
this.isPlaying = false;
});
},
computed: {
isSpinning() {
return this.isPlaying;
}
}
};
Vue.createApp(musicApp).mount("#audioPlayer");
html,
body {
margin: 0;
padding: 0;
font-size: 16px;
}
body * {
box-sizing: border-box;
font-family: "Montserrat", sans-serif;
}
#-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
}
}
#keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.player-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #2998ff;
background-image: linear-gradient(62deg, #2998ff 0%, #5966eb 100%);
}
#audioPlayer {
width: 300px;
height: 300px;
border-radius: 8px;
position: relative;
overflow: hidden;
background-color: #00ca81;
background-image: linear-gradient(160deg, #00ca81 0%, #ffffff 100%);
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
display: flex;
flex-direction: column;
align-items: center;
}
.volume {
color: #ff0057;
opacity: 0.9;
display: inline-block;
width: 20px;
font-size: 20px;
position: absolute;
top: 5px;
right: 6px;
cursor: pointer;
}
.album {
width: 100%;
flex: 1;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
.album-items {
padding: 0 10px;
text-align: center;
}
.cover {
width: 150px;
height: 150px;
margin: auto;
box-shadow: 0px 5px 12px 0px rgba(0, 0, 0, 0.17);
border-radius: 50%;
background: url("https://w7.pngwing.com/pngs/710/955/png-transparent-vinyl-record-artwork-phonograph-record-compact-disc-lp-record-disc-jockey-symbol-miscellaneous-classical-music-sound.png") center top transparent;
background-size: cover;
}
.cover.spinning {
webkit-animation: spin 6s linear infinite;
/* Safari */
animation: spin 6s linear infinite;
}
.info {
width: 100%;
padding-top: 5px;
color: #000;
opacity: 0.85;
}
.info h1 {
font-size: 11px;
margin: 5px 0 0 0;
}
.info h2 {
font-size: 10px;
margin: 3px 0 0 0;
}
.to-bottom {
width: 100%;
margin-top: auto;
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.track {
background-color: #ff0057;
opacity: 0.9;
height: 3px;
width: 100%;
}
.controls {
width: 150px;
display: flex;
height: 60px;
justify-content: space-between;
align-items: center;
}
.controls .navigate {
display: flex;
box-shadow: 1px 2px 7px 2px rgba(0, 0, 0, 0.09);
width: 33px;
height: 33px;
line-height: 1;
color: #ff0057;
cursor: pointer;
background: #fff;
opacity: 0.9;
border-radius: 50%;
text-align: center;
justify-content: center;
align-items: center;
}
.controls .navigate.disabled {
pointer-events: none;
color: #606060;
background-color: #f7f7f7;
}
.controls .navigate.navigate-play {
width: 38px;
height: 38px;
}
.navigate-play .fa-play {
margin-left: 3px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<script src="https://unpkg.com/axios#0.22.0/dist/axios.min.js"></script>
<script src="https://unpkg.com/vue#next"></script>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght#300;500&display=swap" rel="stylesheet">
<div class="player-container">
<div id="audioPlayer">
<span class="volume" #click="toggleMute">
<i v-show="!muted" class="fa fa-volume-up"></i>
<i v-show="muted" class="fa fa-volume-off"></i>
</span>
<div class="album">
<div class="album-items">
<div class="cover" :class="{'spinning' : isSpinning}"></div>
<div class="info">
<h1>{{tracks[trackCount].name}}</h1>
<h2>{{tracks[trackCount].artistName}}</h2>
</div>
</div>
</div>
<div class="to-bottom">
<div class="track"></div>
<div class="controls">
<div class="navigate navigate-prev" :class="{'disabled' : trackCount == 0}" #click="prevTrack">
<i class="fa fa-step-backward"></i>
</div>
<div class="navigate navigate-play" #click="playPause">
<i v-show="!isPlaying" class="fa fa-play"></i>
<i v-show="isPlaying" class="fa fa-pause"></i>
</div>
<div class="navigate navigate-next" :class="{'disabled' : trackCount == tracks.length - 1}" #click="nextTrack">
<i class="fa fa-step-forward"></i>
</div>
</div>
</div>
</div>
</div>
Try to add ended event instead
https://developer.mozilla.org/en-US/docs/Web/API/HTMLAudioElement#events
const audioElement = new Audio('car_horn.wav');
audioElement.addEventListener('ended', () => {
this.isPlaying = false
})

mouseover and mouseout misbehaves when Vue JS transition component is used

I am creating a popup menu as shown below
const menuItem = {
props: ['name', 'val'],
data() {
return {
showChild: false
}
},
template: /*html */ `
<div class="nav-item" #mouseover="showChild=true" #mouseout="showChild=false">
<span v-if="typeof val === 'string' || val instanceof String">
<a :href="val" target="_blank" rel="noopener noreferrer">{{name}}</a>
</span>
<div v-else>
{{name}}
<ul class="popup" v-show="showChild">
<li v-for="(link, name) in val">
<a :href="link" target="_blank" rel="noopener noreferrer">{{name}}</a>
</li>
</ul>
</div>
</div>
`
}
const app = Vue.createApp({
components: {
menuItem
},
data() {
return {
menu: {
'Home': '#',
'Services': {
'Software Development': 'https://www.upwork.com/signup/create-account/client_contact_freelancer?ciphertext=~0142999d8b15001517&BYOC',
'Business Training & Frenchise': 'https://www.badabusiness.com/dd/BIMK003866',
'Organic Marketing Training & Affiliate Program': 'https://leads-arc.web.app/'
},
'Our Apps': {
'All': 'https://play.google.com/store/apps/collection/cluster?clp=igM4ChkKEzUwMDkwNjA5NzAwNjg3NTk4ODIQCBgDEhkKEzUwMDkwNjA5NzAwNjg3NTk4ODIQCBgDGAA%3D:S:ANO1ljIhW_g&gsr=CjuKAzgKGQoTNTAwOTA2MDk3MDA2ODc1OTg4MhAIGAMSGQoTNTAwOTA2MDk3MDA2ODc1OTg4MhAIGAMYAA%3D%3D:S:ANO1ljJmSZ8',
'Featured': 'https://play.google.com/store/apps/dev?id=5009060970068759882',
'Srila Prabhupada Vani': 'https://play.google.com/store/apps/details?id=com.mayank.srilaprabhupadavani',
'ChatEasy - Easy Messaging': 'https://play.google.com/store/apps/details?id=c.kapps.easymessage.free'
},
'Blogs': 'https://mayank-1513.medium.com/',
'Contact Us': 'https://mayank-chaudhari.web.app/',
},
}
}
})
app.mount('#app');
* {
max-width: 1600px;
margin: auto;
transition: 0.2s all cubic-bezier(0.65, 0.05, 0.36, 1);
color: #2c3e50;
cursor: unset;
box-sizing: border-box;
}
body,
html,
#app {
margin: 0;
padding: 0;
font-family: Avenir, Helvetica, Arial, sans-serif;
max-width: 100%;
}
nav {
display: flex;
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 50;
background: transparent;
align-items: center;
align-content: center;
justify-content: center;
}
nav.tr {
background: white;
box-shadow: #5555 0 0 0 2px;
height: 60px;
}
.nav {
display: flex;
align-items: center;
width: 100%;
padding: 15px;
height: 50px;
}
.nav-item {
display: inline-block;
position: relative;
padding: 10px 0;
margin: 20px;
font-weight: bold;
white-space: nowrap;
cursor: pointer;
}
.nav-item * {
text-decoration: none;
}
.nav-item:hover,
.popup li:hover,
.active {
border-bottom: 2px solid #756aee;
transform: translateY(-5%);
filter: drop-shadow(0px 0px 15px #fff);
}
.nav img {
height: 195%;
margin: 0;
cursor: pointer;
}
.nav img:hover {
filter: drop-shadow(-4px 8px 10px #756aee);
transition: all 0.1s;
}
.popup {
position: absolute;
list-style: none;
padding: 15px;
margin-top: 12px;
margin-left: -10px;
line-height: 38px;
border-radius: 5px;
box-shadow: 0px 0px 15px #0005;
background: #fff;
/* display: none; */
}
.nav-item:hover .popup {
display: inherit;
}
<script src="https://unpkg.com/vue#3.0.11/dist/vue.global.js"></script>
<div id="app">
<nav class="tr">
<div class="nav">
<span ref="spacer" class="spacer"></span>
<menu-item :name="key" :val="val" v-for="(val, key) in menu" :key="'menu' + key" />
</div>
</nav>
</div>
This works well. However, when I try to add transition component and enclose ul.popup in transition component, it does not behave properly and opacity becomes 0 even when mouse is on the popup menu. Below is the snippet showing this.
const menuItem = {
props: ['name', 'val'],
data() {
return {
showChild: false
}
},
template: /*html */ `
<div class="nav-item" #mouseover="showChild=true" #mouseout="showChild=false">
<span v-if="typeof val === 'string' || val instanceof String">
<a :href="val" target="_blank" rel="noopener noreferrer">{{name}}</a>
</span>
<div v-else>
{{name}}
<transition name="fade">
<ul class="popup" v-show="showChild">
<li v-for="(link, name) in val">
<a :href="link" target="_blank" rel="noopener noreferrer">{{name}}</a>
</li>
</ul>
</transition>
</div>
</div>
`
}
const app = Vue.createApp({
components: {
menuItem
},
data() {
return {
menu: {
'Home': '#',
'Services': {
'Software Development': 'https://www.upwork.com/signup/create-account/client_contact_freelancer?ciphertext=~0142999d8b15001517&BYOC',
'Business Training & Frenchise': 'https://www.badabusiness.com/dd/BIMK003866',
'Organic Marketing Training & Affiliate Program': 'https://leads-arc.web.app/'
},
'Our Apps': {
'All': 'https://play.google.com/store/apps/collection/cluster?clp=igM4ChkKEzUwMDkwNjA5NzAwNjg3NTk4ODIQCBgDEhkKEzUwMDkwNjA5NzAwNjg3NTk4ODIQCBgDGAA%3D:S:ANO1ljIhW_g&gsr=CjuKAzgKGQoTNTAwOTA2MDk3MDA2ODc1OTg4MhAIGAMSGQoTNTAwOTA2MDk3MDA2ODc1OTg4MhAIGAMYAA%3D%3D:S:ANO1ljJmSZ8',
'Featured': 'https://play.google.com/store/apps/dev?id=5009060970068759882',
'Srila Prabhupada Vani': 'https://play.google.com/store/apps/details?id=com.mayank.srilaprabhupadavani',
'ChatEasy - Easy Messaging': 'https://play.google.com/store/apps/details?id=c.kapps.easymessage.free'
},
'Blogs': 'https://mayank-1513.medium.com/',
'Contact Us': 'https://mayank-chaudhari.web.app/',
},
}
}
})
app.mount('#app');
* {
max-width: 1600px;
margin: auto;
transition: 0.2s all cubic-bezier(0.65, 0.05, 0.36, 1);
color: #2c3e50;
cursor: unset;
box-sizing: border-box;
}
body,
html,
#app {
margin: 0;
padding: 0;
font-family: Avenir, Helvetica, Arial, sans-serif;
max-width: 100%;
}
nav {
display: flex;
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 50;
background: transparent;
align-items: center;
align-content: center;
justify-content: center;
}
nav.tr {
background: white;
box-shadow: #5555 0 0 0 2px;
height: 60px;
}
.nav {
display: flex;
align-items: center;
width: 100%;
padding: 15px;
height: 50px;
}
.nav-item {
display: inline-block;
position: relative;
padding: 10px 0;
margin: 20px;
font-weight: bold;
white-space: nowrap;
cursor: pointer;
}
.nav-item * {
text-decoration: none;
}
.nav-item:hover,
.popup li:hover,
.active {
border-bottom: 2px solid #756aee;
transform: translateY(-5%);
filter: drop-shadow(0px 0px 15px #fff);
}
.nav img {
height: 195%;
margin: 0;
cursor: pointer;
}
.nav img:hover {
filter: drop-shadow(-4px 8px 10px #756aee);
transition: all 0.1s;
}
.popup {
position: absolute;
list-style: none;
padding: 15px;
margin-top: 12px;
margin-left: -10px;
line-height: 38px;
border-radius: 5px;
box-shadow: 0px 0px 15px #0005;
background: #fff;
/* display: none; */
}
.nav-item:hover .popup {
display: inherit;
}
.fade-enter-active,
.fade-leave-active {
transition: all .5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
<script src="https://unpkg.com/vue#3.0.11/dist/vue.global.js"></script>
<div id="app">
<nav class="tr">
<div class="nav">
<span ref="spacer" class="spacer"></span>
<menu-item :name="key" :val="val" v-for="(val, key) in menu" :key="'menu' + key" />
</div>
</nav>
</div>
How do I fix this issue?
Try to use
#mouseenter and #mouseleave combination
There was issue with the event that I was listening to. Adding a detailed answer here for benefit of others looking for solution to similar question.
See the WC3 documentation here
The mouseover event triggers when the mouse pointer enters the div element, and its child elements.
The mouseenter event is only triggered when the mouse pointer enters the div element.
changing #mouseover to #mouseenter and #mouseout to #mouseleave solves the issue.

Struggling with footer and modal

I am struggling with the footer when the modal pops up. The footer doesn't get the opacity that the rest of the HTML page gets. There's anyway to fix it?
PS: It may not be clear in the snippet, so here's the image of the problem:
Codes:
/*Abre e fecha o modal*/
const Modal = {
open(){
document.querySelector('.modal-overlay').classList.toggle('active')
}
}
const transactions = [
{
id: 1,
description: 'Luz',
amount: -50000,
date: '23/01/2021'
},
{
id: 2,
description: 'Website',
amount: 500000,
date: '23/01/2021'
},
{
id: 3,
description: 'Internet',
amount: -2000,
date: '23/01/2021'
},
{
id: 3,
description: 'App',
amount: 2000,
date: '23/01/2021'
},
]
//transaçoes
const Transaction = {
entry(){
//grana que entra
},
out(){
//grana que subtrai
},
total(){
//entry - out
}
}
/* const Modal = {
open(){
document.querySelector('.modal-overlay').classList.add('active')
},
close(){
document.querySelector('.modal-overlay').classList.remove('')
}
}
*/
//criando o HTML pelo JavaScript
const DOM = {
transactionsCointainer: document.querySelector('#data-table tbody'),
addTransaction(transaction, index){
const tr = document.createElement('tr')
tr.innerHTML = DOM.innerHTMLTransaction(transaction)
DOM.transactionsCointainer.appendChild(tr)
console.log(tr.innerHTML)
},
innerHTMLTransaction(transaction) {
const CSSclass = transaction.amount> 0 ? "income" : "expense"
const amount = Utils.formatCurrency(transaction.amount)
const html =
`
<td class="description">${transaction.description}</td>
<td class="${CSSclass}">${amount}</td>
<td class="date">${transaction.date}</td>
<td>
<img src="/assets/minus.svg" alt="Remover Transação">
</td>
`
return html
}
}
const Utils = {
formatCurrency(value){
const signal = Number(value) < 0 ? "-" : ""
value = String(value).replace(/\D/g, "")
value = Number(value) / 100
value = value.toLocaleString("pt-br", {
style: "currency",
currency: "BRL"
})
return signal + value
}
}
transactions.forEach(function(transaction){
DOM.addTransaction(transaction)
})
#import url('https://fonts.googleapis.com/css2?family=Nunito:ital,wght#0,400;0,700;1,400&display=swap');
:root {
--color-dark : #363f5f;
--color-green : #2D4A22;
--color-light-green: #49aa26;
}
* {
padding: 0;
margin : 0;
border : 0;
}
body {
font-family : 'Nunito', sans-serif;
background-color: #f0f2f5;
}
html {
font-size: 93.75%;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
.container {
width : min(90vw, 800px);
margin: 0 auto;
}
/* === HEADER === */
header {
background: var(--color-green);
padding : 2rem 0 10rem;
text-align: center;
}
#logo {
color : #fff;
font-weight: 100;
}
/*=== TITLES === */
h2{
margin-top : 3.2rem;
margin-bottom: 0.8rem;
color : var(--color-dark);
font-weight : normal;
}
/*=== BALANCE === */
#balance {
margin-top: -8rem;
}
#balance h2 {
color : white;
font-weight: bold;
}
/*=== CARDS === */
.card {
background : white;
padding : 1.5rem 2rem;
border-radius: 0.25rem;
margin-bottom: 2rem;
}
.card h3 {
font-weight: normal;
font-size : 1rem;
display: flex;
align-items: center;
justify-content: space-between;
}
.card p {
font-size : 2rem;
line-height: 3rem;
margin-top : 1rem;
}
.card.total {
background: var(--color-light-green);
color : white;
}
/* === TRANSAÇÕES === */
#transaction {
display: block;
overflow-x: auto;
width: 100%;
}
#data-table {
width : 100%;
border-spacing: 0 0.5rem;
}
table thead tr th:first-child,
table tbody tr td:first-child{
border-radius: 0.25rem 0 0 0.25rem;
}
table thead tr th:last-child,
table tbody tr td:last-child{
border-radius: 0 0.25rem 0.25rem 0;
}
table th {
color : #969CB3;
font-weight : normal;
padding : 1rem 2rem;
text-align : left;
background-color: white;
border-radius : 0.25rem;
font-weight : bold;
}
td.description {
color: var(--color-dark);
}
td.income {
color: #12a454;
}
td.expense {
color: rgb(228, 67, 67);
}
td.date {
color: var(--color-dark);
}
table td {
background : white;
padding : 1rem 2rem;
color : #969CB3;
font-weight: bold;
}
table tbody tr {
opacity: 0.7;
}
table tbody tr:hover{
opacity: 1;
}
button {
width: 100%;
height: 50px;
border: none;
background-color: var(--color-light-green);
color: #fff;
font-weight: bold;
text-transform: uppercase;
cursor: pointer;
border-radius: 0.25rem;
}
button:hover {
background: rgb(0, 140, 0);
}
.button-new {
color: var(--color-light-green);
text-decoration: none;
font-weight: bold;
margin-bottom: .8rem;
display: inline-block;
}
a .button {
width: 100%;
color: white;
}
.button-new:hover {
color: #06bd4c;
transition: color 700ms;
}
td img {
cursor: pointer;
}
/* === MODAL === */
.modal-overlay {
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
position: fixed;
top: 0;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
visibility: hidden;
}
.modal-overlay.active {
opacity: 1;
visibility: visible;
}
.modal {
background: #f0f2f5;
padding: 2.4rem ;
max-width: 500px;
width: 90%;
}
/*=== MODAL FORM === */
#form {
max-width: 500px;
}
#form h2 {
margin-top: 0;
}
input {
border: none;
width: 100%;
border-radius: 0.2rem;
padding: 0.8rem;
}
.input-group {
margin-top: 0.8rem;
}
.input-group small {
opacity: .4;
font-weight: bold;
}
/*=== MODAL BUTTON === */
.input-group-actions {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 1.8rem;
}
.input-group-actions .button,
.input-group.actions button {
width: 48%;
}
.input-group-actions .button {
padding: 1rem;
width: 100%;
text-align: center;
border: 1px solid #FF4136;
border-radius: 0.25rem;
text-decoration: none;
color: white;
margin-right: 5px;
background-color: #FF4136;
text-transform: uppercase;
font-weight: bold;
}
.input-group-actions .button:hover {
background: rgb(143, 0, 0);
}
/* === FOOTER === */
footer {
text-align: center;
margin-top: 20px;
}
/* === RESPONSIVE === */
#media (min-width: 800px) {
html { font-size: 87.5%;
}
#balance {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 2rem;
}
}
#media (min-width: 992px) {
footer{
display: block;
background-color: var(--color-green);
color: #fff;
padding: 2rem 0 10rem;
bottom: 0;
position: absolute;
width: 100%;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>dev.finance$</title>
</head>
<body>
<header>
<img src="/assets/logo.svg" alt="Logo Dev Finance">
</header>
<main class="container">
<section id="balance">
<h2 class="sr-only">Balanço</h2>
<div class="card">
<h3>
<span>Entradas</span>
<img src="/assets/income.svg" alt="Imagem de Entradas">
</h3>
<p>R$ 5.000,00</p>
</div>
<div class="card">
<h3>
<span>Saídas</span>
<img src="/assets/expense.svg" alt="Imagem de Saídas">
</h3>
<p>R$ 2.000,00</p>
</div>
<div class="card total">
<h3>
<span>Total</span>
<img src="/assets/total.svg" alt="Imagem de Total">
</h3>
<p>R$ 3.000,00</p>
</div>
</section>
<section id="transaction">
<h2 class="sr-only">Transações</h2>
+ Nova Transação
<table id="data-table">
<thead>
<tr>
<th>Descrição</th>
<th>Valor</th>
<th>Data</th>
<th></th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</section>
</main>
<div class="modal-overlay">
<div class="modal">
<div id="form">
<h2>Nova Transação</h2>
<form action="">
<div class="input-group">
<label class="sr-only" for="description">Descrição</label>
<input type="text" id="description" name="description" placeholder="Descrição">
</div>
<div class="input-group">
<label class="sr-only" for="amount">Valor</label>
<input type="number" id="amount" name="amount" placeholder="0,00" step="0.01">
<small>Usar o sinal - (negativo) para despesas e , (vírgula) para casas decimais.</small>
</div>
<div class="input-group">
<label class="sr-only" for="date">Data</label>
<input type="date" id="date" name="date">
</div>
<div class="input-group-actions">
Cancelar
<button>Salvar</button>
</div>
</div>
</div>
</div>
<footer>
<img src="/assets/logo-footer.svg" alt="Logo Dev Finance">
</footer>
<script src="scripts.js"></script>
</body>
</html>
Your modal-overlay is displaying behind your footer.
Add a z-index: 1000 to your modal-overlay rule.
.modal-overlay.active {
opacity: 1;
visibility: visible;
z-index: 1000; /* <- add this */
}

How to create my elements in chat using JavaScript [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I'm developing a chat, and I need to create the elements using JavaScript, but I don't have a lot of knowledge about JavaScript, so every time I try, it goes wrong, and I believe this is because of the CSS classes too, it makes it harder for me, I would like someone to help me with this
body {
background-color: black;
}
.chat {
width: calc(100% - 20px);
height: 500px;
/* border: solid 1px pink; */
display: flex;
flex-direction: column;
padding: 10px;
margin: 0 auto 0 auto;
overflow: auto;
}
.messages {
margin-top: 30px;
display: flex;
flex-direction: column;
}
.message {
border-radius: 20px;
padding: 4px 15px;
margin-top: 1px;
margin-bottom: 1px;
display: inline-block;
}
.yours {
align-items: flex-start;
}
.yours .message {
margin-right: 25%;
background-color: purple;
position: relative;
}
.yours .message.last:before {
content: "";
position: absolute;
z-index: 0;
bottom: 0;
left: -7px;
height: 20px;
width: 20px;
background: purple;
border-bottom-right-radius: 15px;
}
.yours .message.last:after {
content: "";
position: absolute;
z-index: 1;
bottom: 0;
left: -10px;
width: 10px;
height: 20px;
background: black;
border-bottom-right-radius: 10px;
}
.mine {
align-items: flex-end;
}
.mine .message {
color: white;
margin-left: 25%;
background: linear-gradient(to bottom, #00D0EA 0%, #0085D1 100%);
background-attachment: fixed;
position: relative;
}
.mine .message.last:before {
content: "";
position: absolute;
z-index: 0;
bottom: 0;
right: -8px;
height: 20px;
width: 20px;
background: linear-gradient(to bottom, #00D0EA 0%, #0085D1 100%);
background-attachment: fixed;
border-bottom-left-radius: 15px;
}
.mine .message.last:after {
content: "";
position: absolute;
z-index: 1;
bottom: 0;
right: -10px;
width: 10px;
height: 20px;
background: black;
border-bottom-left-radius: 10px;
}
.yours .user {
font-size: 0.6em;
color: grey;
margin: 0px 0px 0px 10px;
}
.mine .user {
font-size: 0.6em;
color: grey;
margin: 0px 10px 0px 0px;
}
<div class="chat">
<div class="mine messages">
<div class="user"><span>Me</span></div>
<div class="message last">
Dude
</div>
</div>
<div class="yours messages">
<div class="user"><span>Mark</span></div>
<div class="message">
Hey!
</div>
<div class="message">
You there?
</div>
<div class="message last">
Hello, how's it going?
</div>
</div>
<div class="mine messages">
<!-- <div class="user">Me</div> -->
<div class="message">
Great thanks!
</div>
<div class="message last">
How about you?
</div>
</div>
<div class="yours messages">
<div class="user"><span>Anne</span></div>
<div class="message">
Hello!
</div>
<div class="message">
How are you?
</div>
<div class="message last">
Glad to see you?
</div>
</div>
<div class="mine messages">
<!-- <div class="user">Me</div> -->
<div class="message">
Great thanks!
</div>
<div class="message last">
How about you?
</div>
</div>
</div>
How can I create this elements using JavaScript?
You can render a message transcript by binning them by user and then rendering each of the bins.
Each time the underlying transcript is updated, just re-render the messages.
Edit: I changed:
if (currBin.type === binType(message))
to this:
if (currBin.from === message.from)
Because I was attempting to cache the users so that I only display a new user when they enter the chat (following a previous user) as seen in OP's HTML. You could have a flag to only show "Me" when a bin from the receiving user initially send a sequence of messages.
Demo
const messages = [
{ "from": "Me" , "content": "Dude" },
{ "from": "Mark" , "content": "Hey!" },
{ "from": "Mark" , "content": "You there?" },
{ "from": "Mark" , "content": "Hello, how's it going?" },
{ "from": "Me" , "content": "Great thanks!" },
{ "from": "Me" , "content": "How about you?" },
{ "from": "Anne" , "content": "Hello!" },
{ "from": "Anne" , "content": "How are you?" },
{ "from": "Anne" , "content": "Glad to see you?" },
{ "from": "Me" , "content": "Great thanks!" },
{ "from": "Me" , "content": "How about you?" }
];
const renderMessages = (messages) => {
const binType = (message) => message.from === 'Me' ? 'mine' : 'yours';
const binMessages = (messages) => {
const createBin = (message) => {
return {
'type': binType(message),
'from': message.from,
'messages': [ message ]
};
};
return messages.reduce((bins, message) => {
if (bins.length === 0) {
bins.push(createBin(message));
} else {
const currBin = bins[bins.length - 1];
if (currBin.from === message.from) {
currBin.messages.push(message);
} else {
bins.push(createBin(message));
}
}
return bins;
}, []);
};
const render = (bins) => {
return bins.map(bin => {
return `
<div class="${bin.type} messages">
<div class="user"><span>${bin.from}</span></div>
${bin.messages.map((message, index, all) => {
return `
<div class="message ${index === all.length - 1 ? 'last' : ''}">
${message.content}
</div>`
}).join('')}
</div>
`;
}).join('');
}
return render(binMessages(messages));
}
document.querySelector('.chat').innerHTML = renderMessages(messages);
body {
background-color: black;
}
.chat {
width: calc(100% - 20px);
/* border: solid 1px pink; */
display: flex;
flex-direction: column;
padding: 10px;
margin: 0 auto 0 auto;
overflow: auto;
}
.messages {
margin-top: 30px;
display: flex;
flex-direction: column;
}
.message {
border-radius: 20px;
padding: 4px 15px;
margin-top: 1px;
margin-bottom: 1px;
display: inline-block;
}
.yours {
align-items: flex-start;
}
.yours .message {
margin-right: 25%;
background-color: purple;
position: relative;
}
.yours .message.last:before {
content: "";
position: absolute;
z-index: 0;
bottom: 0;
left: -7px;
height: 20px;
width: 20px;
background: purple;
border-bottom-right-radius: 15px;
}
.yours .message.last:after {
content: "";
position: absolute;
z-index: 1;
bottom: 0;
left: -10px;
width: 10px;
height: 20px;
background: black;
border-bottom-right-radius: 10px;
}
.mine {
align-items: flex-end;
}
.mine .message {
color: white;
margin-left: 25%;
background: linear-gradient(to bottom, #00D0EA 0%, #0085D1 100%);
background-attachment: fixed;
position: relative;
}
.mine .message.last:before {
content: "";
position: absolute;
z-index: 0;
bottom: 0;
right: -8px;
height: 20px;
width: 20px;
background: linear-gradient(to bottom, #00D0EA 0%, #0085D1 100%);
background-attachment: fixed;
border-bottom-left-radius: 15px;
}
.mine .message.last:after {
content: "";
position: absolute;
z-index: 1;
bottom: 0;
right: -10px;
width: 10px;
height: 20px;
background: black;
border-bottom-left-radius: 10px;
}
.yours .user {
font-size: 0.6em;
color: grey;
margin: 0px 0px 0px 10px;
}
.mine .user {
font-size: 0.6em;
color: grey;
margin: 0px 10px 0px 0px;
}
<div class="chat"></div>

How to return an array based on a chosen answer in an object

If i have an object defined as a question, how do i display the corresponding array in my .innerHTML based on the users choice
for example
var questions = [
{
"question": "What area of the world are you thinking of discovering next?",
"answer1": "Europe",
"countryList1": "europeArray",
"answer2": "Asia",
"countryList2": "asiaArray",
"answer3": "America",
"countryList3": "americaArray",
"answer4": "Australia",
"countryList4": "australiaArray"
},
]
i have tried several functions but cannot seem to display the correct array i have created for the selected continents, example functions i have created below:
var test_asia = asiaArray
var test_america = americaArray
var test_europe = europeArray
var test_australia = australiaArray
var chosenPlace = function test(questions){
if (questions[0].answer1 == 'Europe') {
return test_europe;
} else if (questions[0].answer2 == 'Asia') {
return test_asia;
} else if (questions[0].answer3 == 'America') {
return test_america;
} else {
return test_australia;
}
}
result.innerHTML =
`<h1 class="final-score">Our Recommendations:</h1>
<div class="summary">
<p><br></br></p>
<p>${chosenPlace()}</p>
</div>
I will do something like that :
const questions =
[ { question: 'What area of the world are you thinking of discovering next?'
, answer:
[ { Europe : 'europeArray' }
, { Asia : 'asiaArray' }
, { America : 'americaArray' }
, { Australia: 'australiaArray' }
] }
, { question: 'Who do you intend to travel with?'
, answer:
[ { 'Myself' : '6' }
, { 'My Partner' : '3' }
, { 'My Family' : '1' }
, { 'My Friends' : '6' }
] }
]
get answer on this way:
const questions =
[ { question: 'What area of the world are you thinking of discovering next?'
, answers :
[ { Europe : 'europeArray' }
, { Asia : 'asiaArray' }
, { America : 'americaArray' }
, { Australia: 'australiaArray' }
] }
, { question: 'Who do you intend to travel with?'
, answers :
[ { 'Myself' : '6' }
, { 'My Partner' : '3' }
, { 'My Family' : '1' }
, { 'My Friends' : '6' }
] }
]
const formContainer = document.getElementById('form-container')
, AnswerElm = [...formContainer.querySelectorAll('label')]
.map(el=>
({ op:el.querySelector('input')
,sp:el.querySelector('span')
})) ;
console.log('AnswerElm', AnswerElm)
formContainer.onsubmit=e=>e.preventDefault(); // disable form submit
let Q_Num = 0;
const Q_Num_max = questions.length;
function setQuestion(NoQuestion)
{
formContainer.reset();
formContainer.question.value = questions[NoQuestion].question;
AnswerElm.forEach((elm,i) =>
{
elm.sp.textContent = Object.keys(questions[NoQuestion].answers[i])[0]
elm.op.value = Object.values(questions[NoQuestion].answers[i])[0]
})
}
setQuestion(Q_Num)
formContainer.onclick=e=>
{
if (!e.target.matches('button')) return
let elmBt = e.target.textContent
if (elmBt == 'Next')
{
alert( formContainer.answer.value)
Q_Num = ++Q_Num %Q_Num_max
setQuestion(Q_Num)
}
}
/* QUESTIONNAIRE FORM */
.form-body {
background-color: #ffd08a;
display: flex;
height: 900px;
}
#form-container {
box-sizing: border-box;
box-shadow: 1px 1px 1px 1px rgba(0, 0, 0, 0.2);
padding: 3rem;
width: 60%;
margin: 5rem auto;
display: flex;
flex-direction: column;
min-height: 70vh;
background-color: #aea4ee;
border: #666 1px solid;
}
#form-container input[type=radio] {
-ms-transform: scale(1.5); /* IE 9 */
-webkit-transform: scale(1.5); /* Chrome, Safari, Opera */
transform: scale(1.5);
margin-right: 1rem;
}
.title{
margin-bottom: 3rem;
font-weight: 400;
font-size: 2.5rem;
text-align: center;
text-transform: uppercase;
}
#form-container output[name=question] {
margin: 2rem 0;
font-size: 1.5rem;
}
#form-container label{
padding: 1rem;
width: 80%;
border-radius: 5px;
transition: all 0.3s;
}
#form-container label:hover +span {
background: rgba(255, 255, 255, 0.4);
}
#form-container label input:checked +span {
background: #08038c6c;
color: #000;
}
#controls > * {
margin: 1rem;
margin-top: 3rem;
}
#controls button {
padding: 1rem 2rem;
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
border-radius: 5px;
font-size: 1rem;
font-weight: 300;
outline: none;
transform: scale(0.98);
transition: all 0.2s;
color: #494949;
text-transform: uppercase;
text-decoration: none;
background: #ffffff;
border: 4px solid #494949;
display: inline-block;
transition: all 0.4s ease 0s;
}
#controls button:hover {
color: #ffffff;
font-weight: bold;
background: #7c00ff;
border-color:#7c00ff;
transition: all 0.4s ease 0s;
cursor: pointer;
}
button.restart {
background: orange;
color: #00000050;
font-size: 2rem;
margin-bottom: 1rem;
transition: all 0.4s;
}
button.restart:hover {
color: #00000098;
cursor: pointer;
}
.result {
display: flex;
flex-direction: column;
padding: 2rem;
justify-content: center;
align-items: center;
text-align: center;
font-size: 2.5rem;
min-height: 80vh;
}
.final-score {
color: #00000099;
}
.summary {
font-size: 1rem;
text-shadow: 1px 1px #ffffff50;
color: #00000099;
background: rgba(255, 255, 255, 0.4);
border-radius: 5px;
display: flex;
flex-direction: column;
align-items: flex-start;
padding: 0 2rem;
margin-bottom: 2rem;
}
.summary h1 {
align-self: center;
}
<section class="form-body">
<form id="form-container">
<div class="title">Questions</div>
<output name="question"></output>
<label class="option">
<input type="radio" name="answer" value=""> <span></span>
</label>
<label class="option">
<input type="radio" name="answer" value=""> <span></span>
</label>
<label class="option">
<input type="radio" name="answer" value=""> <span></span>
</label>
<label class="option">
<input type="radio" name="answer" value=""> <span></span>
</label>
<!-- BUTTONS -->
<div id="controls">
<button>Previous</button>
<button>Next</button>
</div>
</form>
</section>
<div id="result"></div>

Categories

Resources