Using font awesome on Vanilla JavaScript - javascript

I am trying to use a Font Awesome icon to appear next the string of an array. Is it possible? Would I need a library for this?
Thanks a lot
I have tried these (inside and outside the "", which all break my code.
e.innerHTML = '<i class="fas fa-venus" aria-hidden="true"></i>'
e.append('<i class="fas fa-mars" aria-hidden="true"></i>');
var quotesAm = [
/*I need the icons to appear before each of these words below*/
"Hemlaɣk.<br>Hemlaɣkem."
]
let uniqueRandomGenerator = n => {
let set = new Set() // Use Set to remove any duplicates as keep adding #
while (set.size < n) set.add(Math.floor(Math.random() * n)) // Keep adding #
return Array.from(set)
}
let randomQuotes = uniqueRandomGenerator(quotesAm.length), last = 0
function newQuoteAm() {
document.getElementById('amQuoteDisplay').innerHTML = quotesAm[randomQuotes[last]];
last = last == randomQuotes.length - 1 ? 0 : last + 1
}
<h1 class="tamazight-tifinaghe">Amaziɣ Daily</h1><br>
<div id="amQuoteDisplay">
<!--Amaziɣ quotes display here-->
</div>
<div align="left">
<button onclick="newQuoteAm()">Next</button>
</div>
</div>
</div>
<script src="testAm.js"></script>
<script src="https://kit.fontawesome.com/3cb9f76276.js"></script>

You can make your array of quotes to be array of objects with icon specified for each element:
var quotesAm = [
{ label: 'a', icon: 'fa-venus' },
{ label: 'b', icon: 'fa-mars' },
{ label: 'c', icon: 'fa-box' },
{ label: 'd', icon: 'fa-bath' },
{ label: 'e', icon: 'fa-bolt' },
{ label: 'f', icon: 'fa-at' },
]
let uniqueRandomGenerator = n => {
let set = new Set() // Use Set to remove any duplicates as keep adding #
while (set.size < n) set.add(Math.floor(Math.random() * n)) // Keep adding #
return Array.from(set)
}
let randomQuotes = uniqueRandomGenerator(quotesAm.length), last = 0
function newQuoteAm() {
let quote = quotesAm[randomQuotes[last]]
document.getElementById('amQuoteDisplay').innerHTML = `<span><i class="fas ${quote.icon}" aria-hidden="true"></i> ${quote.label}</span>`
last = last == randomQuotes.length - 1 ? 0 : last + 1
}
<h1 class="tamazight-tifinaghe">Amaziɣ Daily</h1><br>
<div id="amQuoteDisplay">
<!--Amaziɣ quotes display here-->
</div>
<div align="left">
<button onclick="newQuoteAm()">Next</button>
</div>
<script src="https://kit.fontawesome.com/3cb9f76276.js"></script>
If you want to have only one icon then use the same approach just at the newQuoteAm function hard that class set in there and keep your array of strings not objects.

Related

How to make a specific button appear in one of four places?

I want to write a script for a quiz, where there are 4 answers from which only one is correct. I want to make the correct answer appear at random position in one of the 4 buttons, while the rest will be wrong answers. Here's what I've got:
const questions = [
{name: "question1"},
{name: "question2"},
{name: "question3"}
];
function shuffle(questions) {
let currentIndex = questions.length,
randomIndex;
while (currentIndex != 0) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
[questions[currentIndex], questions[randomIndex]] = [
questions[randomIndex], questions[currentIndex]
];
}
return questions;
}
function start() {
document.getElementById("start").style.display = "none";
shuffle(questions);
document.getElementById("question").innerHTML = questions[0].name;
document.getElementById("correctanswer").style.display = "block";
document.getElementById("wronganswer1").style.display = "block";
document.getElementById("wronganswer2").style.display = "block";
document.getElementById("wronganswer3").style.display = "block";
}
function shownextquestion() {
shuffle(questions);
document.getElementById("question").innerHTML = questions[0].name;
}
function correct() {
document.getElementById("answerstatus") = "Correct Answer!";
}
function incorrect() {
document.getElementById("answerstatus") = "Wrong Answer";
}
<button id="start" onclick="start()">start test</button>
<div id="question"></div>
</br>
<button style="display:none;" id="correctanswer" onclick="shownextquestion();correct()">Correct Answer</button>
</br>
<button style="display:none;" id="wronganswer1" onclick="shownextquestion();incorrect()">Wrong Answer</button>
</br>
<button style="display:none;" id="wronganswer2" onclick="shownextquestion();incorrect()">Wrong Answer</button>
</br>
<button style="display:none;" id="wronganswer3" onclick="shownextquestion();incorrect()">Wrong Answer</button>
</br>
<div id="answerstatus"></div>
Basically, I want the "correctanswer" button to appear randomly in one of the four spaces, while the other three are "wronganswer" 1,2 and 3.
I'm not sure I get the problem correctly. You say you want to shuffle the answers but in your code you try to shuffle the questions.
Assuming you really want to shuffle the answers to a question, I would recommend to choose a different approach:
Define an array of Question objects providing an array with possible answers to the question
Write a function which creates the answer buttons dynamically
Use a shuffle function to shuffle the answers before rendering
/** All questions with possible answers. */
const questions = [
{
name: 'question1',
text: 'Question 1?',
answers: [
{ text: 'Some wrong answer', correct: false },
{ text: 'Another wrong answer', correct: false },
{ text: 'One more wrong answer', correct: false },
{ text: 'The correct answer', correct: true }
]
},
{
name: 'question2',
text: 'Question 2?',
answers: [
{ text: 'Some wrong answer', correct: false },
{ text: 'Another wrong answer', correct: false },
{ text: 'One more wrong answer', correct: false },
{ text: 'The correct answer', correct: true }
]
},
{
name: 'question3',
text: 'Question 3?',
answers: [
{ text: 'Some wrong answer', correct: false },
{ text: 'Another wrong answer', correct: false },
{ text: 'One more wrong answer', correct: false },
{ text: 'The correct answer', correct: true }
]
}
];
/** The index of the question currently being displayed */
let questionIndex = 0;
/** Fisher-Yates shuffle function for shuffling arbitrary arrays */
function shuffle(array) {
const result = [...array];
for (let i = result.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
let temp = result[i];
result[i] = result[j];
result[j] = temp;
}
return result;
}
/**
* Checks for a given question whether the given answer is correct,
* displays an appropriate message, and shows the button for jumping
* to the next question.
*/
function checkAnswer(question, answer) {
document.getElementById('answerstatus').style.display = "block";
const elAnswerStatusText = document.getElementById('answerstatus-text');
if (answer.correct) {
elAnswerStatusText.innerHTML = 'Correct answer!';
}
else {
elAnswerStatusText.innerHTML = 'Wrong answer!';
}
}
/**
* Creates a DOM representation for a question and its answers.
* This function first shuffles the possible answers and creates
* a button element for each, adding an event handler that calls
* function `checkAnswer()`.
*/
function createQuestion(question) {
const elQuestion = document.getElementById("question");
elQuestion.innerHTML = "";
const elQuestionText = elQuestion.appendChild(document.createElement("div"));
elQuestionText.innerHTML = question.text;
for (const answer of shuffle(question.answers)) {
const elButton = elQuestion.appendChild(document.createElement("button"));
elButton.innerHTML = answer.text;
elButton.addEventListener('click', () => checkAnswer(question, answer));
}
}
function showNextQuestion() {
questionIndex = (questionIndex + 1) % questions.length;
document.getElementById('answerstatus').style.display = 'none';
createQuestion(questions[questionIndex]);
}
function start() {
document.getElementById("start").style.display = 'none';
document.getElementById("question").style.display = 'block';
createQuestion(questions[0]);
}
button {
display: block;
width: 200px;
margin-top: 10px;
}
<html>
<head>
</head>
<body>
<button id="start" onclick="start()">start test</button>
<div id="question" style="display: none">
<div id="question-text">
</div>
</div>
<br>
<div id="answerstatus" style="display: none">
<div id="answerstatus-text">
</div>
<br>
<button onclick="showNextQuestion()">Next Question</button>
</div>
</body>
</html>
To also shuffle the buttons you can use your shuffle() function. For that you need to push the buttons to an array:
const buttons = document.querySelectorAll('button.answer');
let button_array = [];
for (let i = 0; i < buttons.length; i++) {
button_array[i] = buttons[i];
}
After shuffling you remove the buttons and append them in the new order. To get this done you could wrap the answer buttons in a container:
<div id="answers">
<button class="answer" id="correctanswer" onclick="shownextquestion();correct()">Correct Answer</button>
...
</div>
That container can simply be emptied with innerHTML = '' and you can easily append the new ordered buttons to it:
shuffle(button_array);
document.getElementById("answers").innerHTML = '';
for (let i = 0; i < button_array.length; i++) {
document.getElementById("answers").append(button_array[i]);
}
The container has also the advantage that you can hide and show all answer buttons at once instead of doing that for every single answer button:
function start() {
document.getElementById("start").style.display = "none";
document.getElementById("answers").style.display = "block";
...
Furthermore are you doing the same thing in start() and shownextquestion() twice, regarding shuffling and inserting the questions. Therefor it's better to put that code in shownextquestion() and call that function in start(). Especially because the code gets bigger when you add the shuffling and inserting for the buttons.
Working example: (i exchanged the inline-styles and the br-tags with CSS-styles and toggled in JavaScript a class instead of manipulating the style-attribute)
const questions = [
{name: "question1"},
{name: "question2"},
{name: "question3"}
];
const buttons = document.querySelectorAll('button.answer');
let button_array = [];
for (let i = 0; i < buttons.length; i++) {
button_array[i] = buttons[i];
}
function shuffle(elements) {
let currentIndex = elements.length,
randomIndex;
while (currentIndex != 0) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
[elements[currentIndex], elements[randomIndex]] = [
elements[randomIndex], elements[currentIndex]
];
}
return elements;
}
function start() {
document.getElementById("start").classList.add("hide");
document.getElementById("answers").classList.remove("hide");
shownextquestion();
}
function shownextquestion() {
shuffle(questions);
document.getElementById("question").innerHTML = questions[0].name;
shuffle(button_array);
document.getElementById("answers").innerHTML = '';
for (let i = 0; i < button_array.length; i++) {
document.getElementById("answers").append(button_array[i]);
}
}
function correct() {
document.getElementById("answerstatus").innerHTML = "Correct Answer!";
}
function incorrect() {
document.getElementById("answerstatus").innerHTML = "Wrong Answer";
}
* {
margin-bottom: 1rem;
}
button {
display: block;
}
.hide {
display: none;
}
<button id="start" onclick="start()">start test</button>
<div id="question"></div>
<div id="answers" class="hide">
<button class="answer" id="correctanswer" onclick="shownextquestion(); correct()">Correct Answer</button>
<button class="answer" id="wronganswer1" onclick="shownextquestion(); incorrect()">Wrong Answer</button>
<button class="answer" id="wronganswer2" onclick="shownextquestion(); incorrect()">Wrong Answer</button>
<button class="answer" id="wronganswer3" onclick="shownextquestion(); incorrect()">Wrong Answer</button>
</div>
<div id="answerstatus"></div>

How to implement multiple tag search across my image gallery with JavaScript?

I have an image gallery with search input , where the user can type image title or image tag and search for matches. I now need to be able to have multiple search for the tags. For example if i type : #tree - > the result will be all the images that have "tree" in their tags (specifically ) , not the ones that partially contains the word, as Tag search should be specific. I need to be able to type : #tree,#sky - > and the output to be all the images that have "tree" and "sky" in them . So far my code executes only the first example .
HTML :
<div class="searchBtn">
<input type="text" id="inputValue" placeholder="Search by name or #tag">
<button onclick="Search()" type="button">Search</button>
</div>
JS:
let filterSearch = $("#inputValue").val().toLowerCase();
function findAllImages(filter, start, itemsCount) {
let photos = [];
let tagSign = "#";
const searchByTag = filterSearch [0] === tagSign;
let searchCondition = searchByTag ? filterSearch.slice(1) : filter;
let newFiltered = imageArrayPhotos.filter(
searchByTag ? image => image.tag.indexOf(searchCondition) >= 0 :
image => image.title.toLowerCase().indexOf(searchCondition) >= 0);
for (let i = start; i < newFiltered.length; i++) {
photos .push(newFiltered [i]);
if (photos.length >= numberOfImages) {
break;
}
}
return photos ;
}
Can i do it with a callback function on let newFiltered = imageArrayPhotos.filter(function() {}) that goes through all the possibilities?
Why don't you use the regex. check if this is feasible.
let searchByTag = true;
let imageArrayPhotos = [{
tag: 'tree',
title: 'tree'
},
{
tag: 'forest',
title: 'tree'
},
{
tag: 'sky',
title: 'sky'
},
{
tag: 'bird',
title: 'bird'
},
{
tag: 'watertree',
title: 'sky'
},
];
let filterSearch = '';
let searchCondition = '';
let pattern;
let newFiltered = [];
function Search() {
newFiltered = [];
filterSearch = '';
searchCondition = '';
filterSearch = $("#inputValue").val();
filterSearch = filterSearch.split(',');
//searchCondition = searchByTag ? filterSearch.slice(1) : filter;
//newFiltered = imageArrayPhotos.filter(checkFilter);
filterSearch.forEach(function(item) {
item = $.trim(item);
searchByTag = item[0] == "#";
pattern = new RegExp("^" + $.trim(item).replace(/#/g, '') + "$");
let itemData = imageArrayPhotos.filter(checkFilter);
if (itemData.length > 0) {
newFiltered = newFiltered.concat(itemData);
}
});
console.log(newFiltered);
}
function checkFilter(image) {
return searchByTag ? pattern.test(image.tag) :
pattern.test(image.title)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="searchBtn">
<input type="text" id="inputValue" placeholder="Search by name or #tag">
<button onclick="Search()" type="button">Search</button>
</div>

Sort array of objects by key values and displaying them on a HTML element

I'm making a movie sorter list, you enter the title and then the rating and it will show you the movies in order by rating. I have an array of objects and I managed to sort the array by rating, but I can't find a way to actually display the array in order on the HTML DOM.
I've tried for loops and forEach's but they don't work the way I want.
const movieTitle = document.querySelector(".movie-title");
const movieRating = document.querySelector(".movie-rating");
const movieList = document.querySelector(".movie-list");
const sortBtn = document.querySelector(".btn");
let movieStorage = [];
function sendMovie() {
if(event.keyCode == 13) {
if(movieTitle.value != "" && movieRating.value != "") {
title = movieTitle.value;
rating = parseInt(movieRating.value);
movieStorage.push({
title: title,
rating: rating
});
// If rating of a is bigger than rating of b return 1, if not return -1
movieStorage.sort((a, b) => (a.rating > b.rating) ? -1 : 1);
console.log(movieStorage);
addMovieToList(title, rating);
movieTitle.value = "";
movieRating.value = "";
} else {
console.log("Fields missing");
}
}
}
function addMovieToList(title, rating) {
const div = document.createElement("div");
div.className = "list-items";
div.innerHTML = `
<div class="item-title">
<p>${title}</p>
</div>
<div class="item-rating">
<p>${rating}</p>
</div>
<div class="item-delete">
<i class="fa fa-trash trash-icon delete"></i>
</div>
`;
movieList.appendChild(div);
}
function sortByRating(element) {
for(let i = 0; i < movieStorage.length; i++) {
element.innerHTML = `
<div class="item-title">
<p>${movieStorage[i].title}</p>
</div>
<div class="item-rating">
<p>${movieStorage[i].rating}</p>
</div>
<div class="item-delete">
<i class="fa fa-trash trash-icon delete"></i>
</div>
`;
}
}
document.addEventListener("click", (e) => {
const deleteIcon = e.target;
const item = document.querySelector(".list-items");
if(deleteIcon.classList.contains("delete")) {
deleteIcon.parentElement.parentElement.remove(item);
}
})
tldr demo
After sorting the array, you need a way to reference movie divs to sort them. There are many ways to do it, what I chose is using id. When you create movie <div>, give it an ID unique for each movie name:
// Simple function to generate hash number for each string
function hashStr(stringValue) {
var hash = 0, i, chr;
if (stringValue.length === 0) return hash;
for (i = 0; i < stringValue.length; i++) {
chr = stringValue.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0; // Convert to 32bit integer
}
return hash;
}
const MOVIES = [
{name: "a", rating: 3},
{name: "b", rating: 6},
{name: "c", rating: 3},
{name: "d", rating: 2},
{name: "e", rating: 1},
];
function showMovies() {
const moviesDiv = document.querySelector("#movies");
for(const movie of MOVIES)
{
const id = "movie-"+hashStr(movie.name);
// If there's no element with the ID, we need to create the DIV for the movie
if(!document.querySelector("#"+id)) {
const elm = document.createElement("div");
elm.appendChild(new Text(movie.name + " ("+movie.rating+"/10)"));
elm.id = id;
elm.classList.add("movie");
moviesDiv.appendChild(elm);
}
}
}
Then, when sorting, you can reference each movie by ID:
// Sort movies using given property (eg. "name")
// The second param determines sort direction
function sortBy(property, ascending=true) {
MOVIES.sort((a,b) =>{
return cmp(a[property], b[property], ascending);
});
// Now after sorting the array, we can sort the HTML elements
const moviesDiv = document.querySelector("#movies");
let lastMovie = null;
for(const movie of MOVIES)
{
const id = "#movie-"+hashStr(movie.name);
const movieDiv = document.querySelector(id);
console.log(id, movieDiv);
// If created
if(movieDiv) {
// remove and append after last processed movie (for the first movie, this will append to top)
moviesDiv.insertBefore(movieDiv, lastMovie);
}
}
}
// Compare string and number, makes no sense for other types
function cmp(a,b, ascending=true) {
if(typeof a=='number' && typeof b == "number") {
return ascending ? a-b : b-a;
}
else if(typeof a=='string' && typeof b == "string"){
return (ascending ? 1 : -1) * a.localeCompare(b);
}
else {
return 0;
}
}
When you add a movie, you just call sort again. You will need to remember the last sorting parameters for that.
Your sort will work fine. The problem is that after you've sorted you can't just display that movie, you have to redisplay the entire list. You're almost there with your sortByRating method, but it doesn't recreate the entire list correctly. Try something like:
function showMoviesList(element) {
let innerHTML = "";
for (let i = 0; i < movieStorage.length; i++) {
innerHTML += `
<div class="item-title">
<p>${movieStorage[i].title}</p>
</div>
<div class="item-rating">
<p>${movieStorage[i].rating}</p>
</div>
<div class="item-delete">
<i class="fa fa-trash trash-icon delete"></i>
</div>
`;
}
element.innerHTML = innerHTML;
}
This resets the inner HTML of the element to the complete movie list in order every time it's called.
Now call showMoviesList(movieList) instead of calling addMovieToList in sendMovie.

Angular List my objects in vertical alphabetical columns

I've seen a couple examples out there but I can't get it to apply to what I am doing. I know it has to be something silly I am missing.
My report objects are sorted like this by description
report { "description" }
a,b,c,d
e,f,g,h
I want
a,d,g,j
b,e,h,k
c,f,i,l
<div class="row">
<div data-ng-repeat="report in reports | orderBy:['description']" >
<div class="col-xs-3"> {{report.description}}</div>
</div>
...
I've tried chunking the data and several other approaches I've seen on here and I get a mix of results. Every 5th row starts anew with the above code, but my order is across (Horizontal) but I need it in 4 columns alphabetical down (Vertical) ....
It certainly can't be as hard as I am making it...
Are you looking for this below logic?
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
</head>
<body>
<div ng-app="app" ng-controller="ctrl">
{{convertedData}}
</div>
<script src="../lib/angular.js"></script>
<script>
var app = angular.module('app', []);
app.controller('ctrl', function ($scope) {
$scope.data = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'];
$scope.convertedData = {};
var length = $scope.data.length;
var rows = length / 3;
for (var j = 0; j < rows; j++)
{
$scope.convertedData[j] = [];
}
for(var i=0;i<length;i++)
{
$scope.convertedData[i%3].push($scope.data[i]);
}
});
</script>
</body>
</html>
you can use the following code to order your reports
$scope.reports = [{ description: 'a' }, { description: 'b' }, { description: 'c' }, { description: 'd' }, { description: 'e' }, { description: 'f' }, { description: 'g' }, { description: 'h' }, { description: 'i' }, { description: 'j' }, { description: 'k' }, { description: 'l' }];
$scope.orderedReports = [];
var j = 0;
var i = j;
while ($scope.reports.length > $scope.orderedReports.length) {
$scope.orderedReports.push($scope.reports[i]);
i = i + 3;
if (i + 1 > $scope.reports.length) {
j = j + 1;
i = j;
}
}
after ordering the data ng-repeat will show reports the way you want them.
<div data-ng-repeat="report in reports" >
<div class="col-xs-3">
{{report.description}}
</div>
</div>

Get name of parent object in array loop

I have the following array of objects:
payload: [
{name: one},
{name: two,
values: {name: five}
},
{name: three},
{name: four}
]
I loop through this in a recursive way, because this depth of the data can change anytime. So name: five can have there own values again.
Now when I loop through the values of an object, I want the name of the parent object. So for name: five I want to get two in a method.
Is there any way to obtain this name?
I use vue.js a a Javascript library.
This is my loop:
<ul>
<div class="row">
<li v-if="value.name" class="col-md-3 indent" #click="toggle">
{{value.name}}:
</li>
<li v-else class="col-md-3 indent" #click="toggle">
{{value.type}}:
</li>
</div>
<div v-show="open" v-if="isValue">
<codeblock-value
v-for="value in value.values"
:value="value">
</codeblock-value>
</div>
</ul>
And I render this loop like this in my parent file:
<div class="row" v-for="value in payload.values">
<codeblock-value
:value="value">
</codeblock-value>
</div>
Keep in mind that there can be multiple objects with values.
function recurse(parentName, obj) {
console.log("Parent name is: " + parentName);
console.log("Obj name is: " + obj.name);
if(obj.values) {
recurse(obj.name, obj.values);
}
}
recurse(payload[1]);
If you can change your payload structure slightly it would make life a bit easier.
JSFIDDLE
JS
var payload = {
name: "payload",
values: [{
name: "one"
}, {
name: "two",
values: [{
name: "five"
}]
}, {
name: "three"
}, {
name: "four"
}]
};
function dig(parent) {
console.log(parent.name);
if (parent.hasOwnProperty('values') && Array.isArray(parent.values)) {
for(var x = 0, len = parent.values.length; x < len; x++){
dig(parent.values[x]);
}
}
}
dig(payload);
UPDATE FOR VUE.JS
Again, changing the data structure allows you to access the parent. In this example, i dynamically generate the test data so that each child node references its parent (I threw in some randomness to generate folders or not).
JSFIDDLE
Data generation JS
var data = {
name: 'My Tree',
children: []
}
var maxDepth = 4;
function createChild(parent, currentDepth){
var childrenValues = ['hello', 'wat', 'test'];
var createChildFolderChance = 0.5;
for(var x = 0, len = childrenValues.length; x < len; x++){
var child = {
name: childrenValues[x],
parent: parent
}
if(Math.random() < createChildFolderChance && currentDepth < maxDepth){
child.children = [];
currentDepth++;
createChild(child, currentDepth)
}
parent.children.push(child);
}
}
createChild(data, 0);
Updated Vue.JS click code
function() {
if (this.isFolder) {
this.open = !this.open
}else{
var firstSiblingWithChildren;
// cycle through current node's parent's children (ie. siblings) and return the name of the first node that has children
for(var x = 0, len = this.model.parent.children.length; x < len; x++){
if(this.model.parent.children[x].hasOwnProperty('children') && Array.isArray(this.model.parent.children[x].children)){
firstSiblingWithChildren = this.model.parent.children[x].name;
break;
}
}
console.log(firstSiblingWithChildren);
}
},

Categories