Better, cleaner way of writing this code? - javascript

I am fairly new to coding and wanted a better (less repetative) way of creating this line of code for my fetch call. I'll add the important parts of the code.
HTML
<form id= 'selectionForm' action="#">
<p>
<label>
<input type="checkbox" class="filled-in" name="Nexflix" id="Nexflix"/>
<span>Netflix</span>
</label>
</p>
</form>
Javascript
document.getElementById('fetch').addEventListener('click', function (element) {
element.preventDefault();
let checkedElements = document.querySelectorAll('#Nexflix:checked');
var options = {
method: "GET",
headers: {
"X-RapidAPI-Key": "using my own",
"X-RapidAPI-Host": "streaming-availability.p.rapidapi.com",
},
};
let outputContainer = document.getElementById('output');
for (let e of checkedElements) {
fetch('https://streaming-availability.p.rapidapi.com/search/basic?country=us&service=netflix&service=netflix&type=movie&genre=18&page=1&output_language=en&language=en', options).then((response) => response.json()).then( (data) => {
outputContainer.appendChild(document.createTextNode('The checked element is : '+e.id));
outputContainer.appendChild(document.createElement("br"));
outputContainer.appendChild(document.createTextNode(data.results[0].title));
outputContainer.appendChild(document.createElement("br"));
outputContainer.appendChild(document.createTextNode('IMDB Rating : ' + data.results[0].imdbRating));
outputContainer.appendChild(document.createElement("br"));
outputContainer.appendChild(document.createTextNode('Year of release : ' + data.results[0].year));
outputContainer.appendChild(document.createElement("br"));
outputContainer.appendChild(document.createTextNode('Overview : ' + data.results[0].overview));
outputContainer.appendChild(document.createElement("br"));
outputContainer.appendChild(document.createTextNode(data.results[1].title));
I would also like to repeat this for other films/results from the API. They give results like
{2 items
"results":[8 items
0:{21 items
"age":10
"backdropPath":"/pYziM5SEmptPW0LdNhWvjzR2zD1.jpg"
"backdropURLs":{...}4 items
"cast":[...]4 items
"countries":[...]1 item
"genres":[...]2 items
"imdbID":"tt9850370"
"imdbRating":64
"imdbVoteCount":1069
"originalTitle":"#AnneFrank. Parallel Stories"
"overview":"One single Anne Frank moves us more than the countless others who suffered just as she did but whose faces have remained in the shadows-Primo Levi. The Oscar®-winning Helen Mirren will introduce audiences to Anne Frank's story through the words in her diary. The set will be her room in the secret refuge in Amsterdam, reconstructed in every detail by set designers from the Piccolo Theatre in Milan. Anne Frank this year would have been 90 years old. Anne's story is intertwined with that of five Holocaust survivors, teenage girls just like her, with the same ideals, the same desire to live: Arianna Szörenyi, Sarah Lichtsztejn-Montard, Helga Weiss and sisters Andra and Tatiana Bucci. Their testimonies alternate with those of their children and grandchildren."
"posterPath":"/hkC4yNDFmW1yQuQhtZydMeRuaAb.jpg"
"posterURLs":{...}7 items
"runtime":92
"significants":[...]2 items
"streamingInfo":{...}1 item
"tagline":""
"title":"#AnneFrank. Parallel Stories"
"tmdbID":"610643"
"video":"FzT7-NfkxLA"
"year":2019
}
1:{...}21 items
2:{...}21 items
3:{...}21 items
4:{...}21 items
5:{...}21 items
6:{...}21 items
7:{...}21 items
Thank you for your time reading this!

Use <p>aragraphs instead of text nodes with many linebreaks inbetween.
You can create them easily in a loop:
const lines = [
'The checked element is : '+e.id,
data.results[0].title,
'IMDB Rating : ' + data.results[0].imdbRating,
'Year of release : ' + data.results[0].year,
'Overview : ' + data.results[0].overview,
data.results[1].title
];
for (const line of lines) {
const paragraph = document.createElement('p');
paragraph.appendChild(document.createTextNode(line));
outputContainer.appendChild(paragraph);
}

Related

JavaScript - Having trouble splicing an item from an array and then re-rendering that array to my page

Essentially, I'm rendering a couple of notes from a notes[] array to my page and adding a button to each note that, when clicked, will remove the note from the array using splice(), then re-render the remaining notes. The button is given an id based on the title of the note (given as input by a user), which is also given a similar id.
This is just some Vanilla JS for a personal project and after looking around this morning I've yet to find a solution that doesn't deal with Vue, Angular, or some other framework/package.
HTML
This is the relevant HTML:
<main>
<div id="container">
<form id="utilities">
<input type="text" name="userTitle" id="user-title" placeholder="Title" autocomplete="off">
<input type="text" name="userBody" id="user-body" placeholder="Body" autocomplete="off">
<div id="buttons">
<input type="submit" name="userSubmit" id="user-submit" value="Submit">
</div>
<input type="text" name="userFilter" id="user-filter" placeholder="Search" autocomplete="off">
</form>
<div id="results"></div>
</div>
</main>
JavaScript
The following JS snippets are all in order, simply broken up for readability and comments.
This is the array I'm passing to and pulling from. It takes a title, a body, an id.
const notes = [{
title: 'Grocery List',
body: 'Buy milk, eggs, and bread',
id: 'grocery-list'
},
{
title: 'Workout Routine',
body: 'running, push-ups, rowing',
id: 'workout-routine'
}
]
This is my function that's getting the title and body text from the user input. It is called in renderNotes().
const results = document.querySelector('#results');
const createElement = function(noteTitle, noteBody) {
const newNote = document.createElement('div');
const newTitle = document.createElement('h3');
const newBody = document.createElement('p');
const newButton = document.createElement('div');
newTitle.textContent = noteTitle;
newTitle.classname = 'note-title';
newBody.textContent = noteBody;
newBody.className = 'note-body';
newButton.className = 'note-button';
newButton.innerHTML = '⮿';
newButton.id = `button-${newTitle.textContent.toLowerCase()}`.replace(' ', '-');
newNote.appendChild(newTitle);
newNote.appendChild(newButton);
newNote.appendChild(newBody);
newNote.className = "note";
newNote.id = newTitle.textContent.toLowerCase().replace(' ', '-');
results.appendChild(newNote);
}
renderNotes() is simply calling createElement() on each note in that array. Then I'm calling it for the first time.
const renderNotes = function(list) {
console.log(list)
list.forEach(function(item) {
createElement(item.title, item.body)
})
}
renderNotes(notes);
This snippet seems to be where I'm failing. It should be getting each button (created during the createElement() calls) and adding an eventListener('click') to it. Then it is supposed to look through each of the notes in notes[], find the one which has a similar id to that of the button pressed, and splice() that note from the notes[] array. Then I simply want to re-render the notes.
document.querySelectorAll('.note-button').forEach(function(button) {
button.addEventListener('click', function(e) {
notes.forEach(function(note) {
if (e.target.id.includes(note.id)) {
notes.splice(notes.indexOf(note), 1)
}
renderNotes(notes)
})
})
});
Actual Results
The notes seem to be spliced from the array consistently, but my problem lies in getting them to re-render. Currently it's just removing the items and then either failing to render or is adding more copies to the page. Edit to add: I'm not receiving any errors in the console, the code is just executing properly as far as the computer is concerned. So I know it's definitely a "Problem Between Chair and Keyboard" error.
Hopefully this makes sense. I appreciate any responses.
You're calling renderNotes(notes) at each iteration not only after the array was mutated.
Try something like:
const clickedNote = notes.find(note => e.target.id.includes(note.id));
notes.splice(notes.indexOf(clickedNote), 1);
renderNotes(notes);
A better solution will be to define notes using let and then do something like this:
notes = notes.filter(note => !e.target.id.includes(note.id));
renderNotes(notes);

Can I make an attribute appear only once in a vue v-for

I have an array of people with associated teams. I want to display all the people in the record, but I only want to display their team name once.
Meaning, if the v-for loop has encountered this particular team name, it should put it in a new temporary array to signify that it should be unique, then when it encounters that team name again, checks it through that temporary array and prevent it from showing again.
Sample HTML Code:
<div id="a-list">
<div v-for="person in people">{{person.Name}}, {{person.Team}}</div>
</div>
Sample Vue Code:
var crew = new Vue({
el: "#a-list",
data: {
people:
[ { "Name": "Richard","Team":"DMS"}, { "Name": "Mark","Team":"VV"}, { "Name": "Steve","Team":"VV"}, {"Name":"Koji","Team":"MZ"}, {"Name":"Jamie","Team":"VV"} ]
}
});
Expected Output:
Richard, DMS
Mark, VV
Steve,
Koji, MZ
Jaimie,
Is this possible to do directly from the v-for loop and not in the JS file?
Edited to show more data that are not sequential
Update: As Fabio has pointed out, the above scenario wouldn't make much sense unless the order of the team is arranged sequentially in the output first. So his answer is correct.
This could be a solution:
<div id="a-list">
<div v-for="(person,index) in people"> {{person.Name}}, {{ ((index == 0) || person.Team != people[index-1].Team) ? person.Team : '' }}</div>
</div>

Javascript array grouped in 3's

I'm trying to parse rss feed.
But what I want to get is:
1) an array grouped in 3 elements. So I have different feed with only its own data grouped together - so it becomes easy to call.
Example :
myArr = [{title, date, content}, {title,date,content}, ....... ]
2) How do I access this array and its elements from outside this function?
I used console.log just to show you guys an idea of what I'm trying to achieve here.
Thanks guys for helping!!
var parser = require('rss-parser');
function myFunc(){
parser.parseURL('https://www.reddit.com/.rss', function(err, parsed) {
console.log(parsed.feed.title);
parsed.feed.entries.forEach(function(entry) {
var items = [];
items.push(entry.title, entry.pubDate, entry.content.split('src=')[1]);
console.log("Title: " + items[0])
console.log("Date: " + items[1])
console.log("Content: " + items[2]);
console.log(" +++++++++++++++ ")
});
});
};
myFunc();
========
OutPut I get :
+++++++++++++++
Title: Brave Floridian workers delivering pizza during hurricane Irma.
Date: 2017-09-10T23:57:01.000Z
Content: "https://b.thumbs.redditmedia.com/4B5QcikKMP8fU90wk6cpR99LSkppsvBIbo88j
4YgysY.jpg" alt="Brave Floridian workers delivering pizza during hurricane Irma.
" title="Brave Floridian workers delivering pizza during hurricane Irma." /> </a
> </td><td> submitted by <a href="https://www.reddit.com/user/Daului
gi51"> /u/Dauluigi51 </a> to <a href="https://www.reddit.com/r/Bikin
iBottomTwitter/"> r/BikiniBottomTwitter </a> <br/> <span><a href="https://i.redd
.it/wuu9mews85lz.jpg">[link]</a></span> <span><a href="https://www.reddit.
com/r/BikiniBottomTwitter/comments/6zbunz/brave_floridian_workers_delivering_piz
za_during/">[comments]</a></span> </td></tr></table>
+++++++++++++++
Title: Redditor Provides Specific, Clear Instructions to Aid a User in Helping P
eople Hit By Hurricane Irma in the U.S. Virgin Islands
Date: 2017-09-11T04:30:26.000Z
Content: undefined
+++++++++++++++
Title: A fellow Florida 501st member posted this on his Facebook
Date: 2017-09-11T00:04:25.000Z
Content: "https://b.thumbs.redditmedia.com/C1Putt8Dihjv31fxqqpwOD3NHMbEddkMUogsW
j4DHLA.jpg" alt="A fellow Florida 501st member posted this on his Facebook" titl
e="A fellow Florida 501st member posted this on his Facebook" /> </a> </td><td>
submitted by <a href="https://www.reddit.com/user/morgul702"> /u/mor
gul702 </a> to <a href="https://www.reddit.com/r/StarWars/"> r/StarW
ars </a> <br/> <span>[link]</span>
<span><a href="https://www.reddit.com/r/StarWars/comments/6zbvyq/a_fellow
_florida_501st_member_posted_this_on_his/">[comments]</a></span> </td></tr></tab
le>
+++++++++++++++
If I understood you well
const items = parsed.feed.entries.map((entry) => ({
title: entry.title,
date: entry.pubDate,
content: entry.content.split('src=')[1],
}));
You are gonna get something like items = [{title, date, content}, {title,date,content}, ....... ]
Promises allow you to access the results of an async function from outside of the callback. I believe this is what you are looking for:
var parser = require('rss-parser');
function myFunc() {
return new Promise((resolve) => { // Promise to allow async access outside of the function
parser.parseURL('https://www.reddit.com/.rss', (err, parsed) => {
resolve(parsed.feed.entries.map(entry => ({ title: entry.title, date: entry.pubDate, content: entry.content.split('src=')[1] }))); // Mapping into objects of title, date and entry
});
});
};
myFunc().then((entries) => {
console.log(entries[0].title); // Output the title of the first result
console.log(entries);
});

AngularJS filter multiple words from a string in a single field

Here the idea of my problem :
Imagine, I have an user looking for a book.
As input, he can try to find the book using book title.
Let's take :
$scope.books = ["Heir Of Earth",
"Woman Of The Solstice",
"Hunters Of Next Year",
"Pilots Without Honor",
"Slaves And A Woman",
"Kings And Heroes",
"Decay Of The End",
"Fortune Of The Land",
"Rejecting My Dreams",
"Painting The Angels"];
Now I take the user query in a string :
example :
$scope.userQueryTitle = "Woman Of";
I want to get all books which where their title contains "Woman" or "Of" but NOT "Woman" AND "Of".
From there, the corresponding result should be this :
$scope.booksResult = ["Heir Of Earth",
"Woman Of The Solstice",
"Hunters Of Next Year",
"Slaves And A Woman",
"Decay Of The End",
"Fortune Of The Land"
];
I found some partial solutions to my problem such as use something like :
View:
<ul ng-repeat="book in booksResult | filter: getBooksFromTitle">
<li>{{book}}</li>
</ul>
Controller:
$scope.getBooksFromTitle = function(book){
return book.match(($scope.userQueryTitle).split(" ")[0]) ||
book.match(($scope.userQueryTitle).split(" ")[1]);
};
It works only if $scope.userQueryTitle contains only 2 words but my problem is what's about if I have 1 or 4 or n word(s) ?
getBooksFromTitle() should be flexible about this but I don't see how to deal with...
Any suggestion ?
Thanks !
This will do it for you:
$scope.getBooksFromTitle = function(book){
var queryWords = $scope.userQueryTitle.split(" ");
for(var i=0;i<queryWords.length;i++){
return book.match(queryWords[i]);
}
};
Change the getBooksFromTitle function as follows,
$scope.getBooksFromTitle = function(book){
return $scope.books.filter(function(e){ return e.match(new RegExp($scope.query.split(" ").join('|'),'i')) });
};
Try to put as many keywords you want, this should solve your problem. Cheers

new to javascript. I am trying to pass the value of a radio button as the name of an object in a javascript function

I have a function called speak. It takes in an object filled with arrays of fragments of sentences and spits out a random phrase. I have two of these objects. Each with unique sentence fragments. Via radio button I would like to be able to choose which object is sent through the function and then push a button to make that happen. When I hard wire the function with one of the buttons it works fine, but that is not what I am going for. I have tried several different suggested methods from this site and others with no luck. The closest I get is when I can get the name of the object into the speak function, but it is only recognized as a string. Here is my html...
<html>
<head>
</head>
<body>
<!-- <script type="text/javascript" src="wrestlingGame.js"></script> -->
<div>
<input type="radio" name="speak" value=ToolBelt.commentary onClick="ToolBelt.handleClick(this)">commentary a<br/>
<input type="radio" name="speak" value=ToolBelt.commentary1 onClick="ToolBelt.handleClick(this)">commentary b<br/>
</div>
<button onclick="ToolBelt.speak()">Commentary</button>
<div id="commentator"></div>
<div id="lif"></div>
</body>
this version is not wired to the 'Commentary' button. It is instead wired to the radio buttons themselves and it does not work correctly. I am posting this because it is my most recent attempt.
Here is my complete javascript including the two objects and the speak function...
var ToolBelt = {
commentary:{
exclamations: ["Wow! ", "Oh no! ", "God almighty! ", "Good Gracious! "],
leadIn: ["That was a nasty ", "What a powerful ", "A horrifying ", "That was an illegal "],
strikes: ["uppercut!", "chairshot!", "Lucy lick!", "monkey punch!", "jab", "Bug meow!", "dropkick!"],
},
commentary1:{
exclamations: ["Moo! ", "Quack! ", "Bark! ", "Growl! "],
leadIn: ["Chupa chup ", "Spaghetti ", "Bubbling ", "Necktie "],
strikes: ["uppercut!", "chairshot!", "Lucy lick!", "monkey punch!", "jab", "Bug meow!", "dropkick!"],
},
handleClick:function(object){
this.speak(object.value);
},
speak:function(i){
var string='';
for(key in i){
var arr = i[key];
var x = this.random(0,arr.length);
string += arr[x] + " ";
}
document.getElementById("commentator").innerHTML = string;
},
random: function(max, min){
return Math.floor(Math.random()*(max-min)+min);
}
};
There are a few problems here.
First, you cannot assign a JavaScript object as the value of an input element. Input element values can be strings only. What we can do with the value is have it contain the key of the ToolBelt object that we want to target:
<input type="radio" name="speak" value="commentary" onClick="ToolBelt.handleClick(this)">commentary a
<br/>
<input type="radio" name="speak" value="commentary1" onClick="ToolBelt.handleClick(this)">commentary b
Next, you must understand what the this is in the onClick handlers on your input elements. In this context, this refers to the DOM element itself. So in the ToolBelt.handleClick method, we will want to get the value from the passed DOM element:
handleClick: function (el) {
this.speak(this[el.value]);
}
This will work, but it will generate commentary whenever we check one of the radio buttons. However, the presence of the "Commentary" button suggests that we want to generate commentary only when this button is clicked. To achieve this, we will have to remove the call to speak in our handleClick method and instead cache the currently selected commentary key:
currentCommentaryKey: null,
handleClick: function (el) {
this.currentCommentaryKey = el.value;
}
Then we will alter the speak method to make use of the cached key (I have added a check to make sure the current commentary key is valid):
speak: function () {
if (!this.hasOwnProperty(this.currentCommentaryKey)) { return; }
var i = this[this.currentCommentaryKey];
/* rest of speak method implementation */
}

Categories