How can I have 2 separate onmessage event handlers run simultaneously? - javascript

I have a webpage with two separate unordered list. After connecting to a websocket, when I send two different messages the live data I get back from the messages only populate the first list, because the onmessage event. I would like data from message 1 to populate list 1 and the data from message 2 to populate list 2 simultaneously.
JavaScript
function sendMessage1(event) {
var input = document.getElementById("messageText1")
ws.send(input.value)
input.value = ''
event.preventDefault()
ws.onmessage = function (event) {
var parsed = JSON.parse(event.data)
var messages = document.getElementById('messages1')
var message = document.createElement('li')
var content = document.createTextNode(JSON.stringify(parsed['data']))
message.appendChild(content)
messages.appendChild(message)
};
}
function sendMessage2(event) {
var input = document.getElementById("messageText2")
ws.send(input.value)
input.value = ''
event.preventDefault()
ws.onmessage = function (event) {
var parsed = JSON.parse(event.data)
var messages = document.getElementById('messages2')
var message = document.createElement('li')
var content = document.createTextNode(JSON.stringify(parsed['data']))
message.appendChild(content)
messages.appendChild(message)
};
HTML
<div class="flex-container">
<div class="flex-child">
<form action="" onsubmit="sendMessage1(event)">
<input type="text" id="messageText1" autocomplete="off" />
<button>Send</button>
</form>
<ul id='messages1'>
<li>Enter Channel Subscription Above</li>
</ul>
</div>
<div class="flex-child">
<form action="" onsubmit="sendMessage2(event)">
<input type="text" id="messageText2" autocomplete="off" />
<button>Send</button>
</form>
<ul id='messages2'>
<li>Enter Channel Subscription Above</li>
</ul>
</div>
</div>

As ultimatum gamer noted, when you do that you overwrite the handler. In order to add several listeners in different places you need some other method:
You can add an event listener
ws.addEventListener('message', event => {
var parsed = JSON.parse(event.data)
var messages = document.getElementById('messages2')
var message = document.createElement('li')
var content = document.createTextNode(JSON.stringify(parsed['data']))
message.appendChild(content)
messages.appendChild(message)
};
// You can add as many as you want!
ws.addEventListener('message', event => {
var parsed = JSON.parse(event.data)
var messages = document.getElementById('messages2')
var message = document.createElement('li')
var content = document.createTextNode(JSON.stringify(parsed['data']))
message.appendChild(content)
messages.appendChild(message)
You can even specify it some potentially useful options
Unrelated to the question, you should wrap the duplicate functionality in its own function, this will save you time: If you change something in one function you need to change it in the other, this will be a headache and a source of annoying bugs.
Also, your code doesn't do what you think it will do, you need to parse it somehow and call the appropiate function. For example:
Create the message: const message = {id: 'messages2', data: input.value}
send message
receive message ws.addEventListener('message', event =>{})
parse message and call appropiate function:
event => {
const parsed = JSON.parse(event.data)
const message = document.createElement('li')
const content = document.createTextNode(JSON.stringify(parsed['data']))
message.appendChild(content)
const messages = document.getElementById(message.id)
messages.appendChild(message)
};

You have to put, the onmessage function, out of the sendMessage function. Because each time you do ws.onmessage = functions () {} you overwrite the function.

Related

How to avoid values from an object after inserting from an input form to be inserted repeatedly?

I am inserting the values of an input into an array of objects. Then, I want to get those values e show inside the HTML. Inserting each value inside the object is not the problem, every time I click the button, each value is successfully added. When I console.log() the array, it shows only one of each value added. The problem is when I try to show the content of the object inside the HTML element, it inserts all the data from the object over and over again, but I just want to add the last value added and keep what was previously inserted, not to add everything again.
What am I doing wrong?
This is my HTML
<main>
<div class="add-recipes">
<form id="form">
<h2>Add Recipe</h2>
<div class="input-wrapper">
<div class="text-input-wrapper">
<label for="title"
>Title
<input type="text" name="title" id="recipe-title" />
</label>
</div>
</div>
<button id="send-recipe-btn" type="submit">Send Recipe</button>
</form>
</div>
<div class="recipes-container"></div>
</main>
This is my JS File
let recipes = [];
const addRecipe = e => {
e.preventDefault();
let recipe = {
title: document.getElementById('recipe-title').value
};
recipes.push(recipe);
document.querySelector('form').reset();
recipes.forEach(e => {
const recipeContainer = document.querySelector('.recipes-container');
const recipeTitle = document.createElement('div');
recipeTitle.classList.add('recipe-title');
recipeContainer.append(recipeTitle);
recipeTitle.textContent = e.title;
});
console.log(recipes);
};
document.getElementById('send-recipe-btn').addEventListener('click', addRecipe);
Thanks for any tip or help to solve this.
Have the forEach()loop to start before recipeTitle.textContent = e.title;
let recipes = [];
const addRecipe = e => {
e.preventDefault();
let recipe = {
title: document.getElementById('recipe-title').value
};
recipes.push(recipe);
document.querySelector('form').reset();
const recipeContainer = document.querySelector('.recipes-container');
const recipeTitle = document.createElement('div');
recipeTitle.classList.add('recipe-title');
recipeContainer.append(recipeTitle);
recipes.forEach(e => {
recipeTitle.textContent = e.title;
});
console.log(recipes);
};
document.getElementById('send-recipe-btn').addEventListener('click', addRecipe);
In your event handler, you are looping over the recipes array and creating a new element every single time the button is pressed.
Just remove the loop and it will work properly

How do I make the Tasks that are created local storage for the browser?

I am trying to make the tasks that have been created save to the user's local storage within the browser. Is it also possible to show me how to make the delete button remove the created task?
/************************************
* creates an object of elements needed *
************************************/
const elements = {
form: document.querySelector("#new-task-form"),
input: document.querySelector("#new-task-input"),
list: document.querySelector("#tasks"),
cal: document.querySelector("#calendar")
}
/****************************
* Generates an ID for task *
****************************/
const createId = () => `${Math.floor(Math.random() * 10000)}-${new Date().getTime()}`
/**********************************************
* function that creates the HTML elements *
**********************************************/
const createTask = () => {
const id = createId()
const task = elements.input.value;
const date = elements.cal.value;
if(!task && !date) return alert("Please fill in task and select date");
if(!task) return alert("Please fill in task");
if(!date) return alert("Please select date");
const tasks = document.createElement("div");
tasks.innerHTML = `
<button class = "sort">Sort</button>
<div class="task" data-id = "${id}">
<div class="content">
<input type ="checkbox" class="tick">
<input type ="text" class = "text" id = "text" value="${task}" readonly>
<label class = "due-date" for ="text">${date}</label>
<input type ="date" class = "date" id = "date">
</div>
<div class = "actions">
<button class="edit" data-id="${id}">Edit</button>
<button class="delete" data-id="${id}">Delete</button>
</div>
</div>
`
elements.list.appendChild(tasks)
listen()
return tasks
}
/********************************************
* Marks tasks as complete with checkbox *
********************************************/
function listen(){
let allCheckboxes = document.querySelectorAll('.tick')
allCheckboxes.forEach(checkbox =>{
checkbox.addEventListener('change',(e)=>{
let parentElem=e.target.parentElement
if(e.target.checked){
parentElem.style.textDecoration = "line-through"
}
else{
parentElem.style.textDecoration = "none"
}
});
});
}
/**************************************************************
* Event that listens for the edit,save and delete buttons *
**************************************************************/
elements.list.addEventListener('click',event => {
const {target} = event;
const {id} = target.dataset;
const task = id ? document.querySelector(`[data-id="${id}"]`):null;
const type = {
edit: event.target.classList.contains('edit'),
delete: event.target.classList.contains('delete')
}
const isFromSaveLabel = target.innerText.toLowerCase() === 'save'
//Checking to see if buttons are pressed
if(task && type.edit && isFromSaveLabel){
const text = task.querySelector('text')
target.innerText = 'Edit'
text.setAttribute('readonly', true)
return
};
if(task && type.edit){
const text = task.querySelector('text')
target.innerText = 'save'
text.removeAttribute('readonly')
text.focus()
return
};
if(task && type.delete){
return
}
});
/*******************************************************************
* Submits the HTML elements to have the lists submited and created*
*******************************************************************/
const submitHandler = (event) =>{
event.preventDefault();
createTask();
}
elements.form.addEventListener("submit", submitHandler);
The end result that I would like to achieve is to make the created list local storage so that if the page refreshes the tasks remain. and having the task delete when clicking the delete button
You can find the jsfiddler here: https://jsfiddle.net/blaze92/seLvzd1h/2/
I Had Done The Exact Same Project In The Past My Project. So How You Can Do It Can Be Like The Following:
Since You Already Have A Method For Creating A Task createTask. modify this function in a way such that you can pass custom parameters. This allows you to create a method to add notes from the localstorage to your page.
Then You Need To Take All The Tasks get their info using document.getElementByClassName or document.getElementByID and then add it to a JSON Object/Array.
Then You Can Convert this JSON Object/Array Into String Using JSON.Stringify. Now You Can Store This String Into Localstorage using localStorage.setItem()
Now You Can Add A Event Listener To Detect Page Load and then get this String from localhost as localStorage.getItem() and then convert it back to a JSON Object.
Then Create A For Loop Which Iterates Through all the elements of this object/array and call the method to createTask with the parameters from the object.

How do I save to local storage via vanilla JS

I can't seem to get local storage to work. The goal is to keep the todo list items on the page upon refresh. Every time I refresh the page it goes poof. The syntax seems right.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TODO LIST</title>
<link rel="stylesheet" href="./styles/style.css">
</head>
<body>
<main id="main">
<h1>THE TO-DO LIST:</h1>
<form action="" id="add-task">
<label for="todo">Add Task:</label>
<input type="text" id="todo">
<button>Add Task</button>
</form>
<p class="center">To complete task, click on text.</p>
<ul id="task-list">
<li class="task-complete">example_1 <button>Remove Task</button></li>
</ul>
</main>
<script src="./script/index.js"></script>
</body>
</html>
const form = document.querySelector('#add-task');
const input = document.querySelector('#todo');
const taskList = document.querySelector('#task-list');
let taskID = 0;
taskList.addEventListener('click', function(e) {
if (e.target.tagName === 'BUTTON') {
e.target.parentElement.remove();
let inputTask = document.getElementById('todo');
localStorage.setItem('email', inputTask.value);
} else if (e.target.tagName === 'LI') {
e.target.classList.toggle('task-complete');
}
});
form.addEventListener('submit', function(e) {
e.preventDefault();
console.log(input.value);
const newTask = document.createElement('li');
const removeBtn = document.createElement('button');
let savedInput = input.value;
removeBtn.innerText = 'Remove Task';
newTask.innerText = input.value;
newTask.appendChild(removeBtn);
taskList.appendChild(newTask);
input.value = '';
console.log(localStorage);
});
.task-complete {
text-decoration: line-through;
}
Joshua, here are a few things from looking at your sample:
First, you're setting the localStorage to a single item, with the current input value, not a collection of tasks like an array
It also seems that you're not getting the saved data on page reload, that's why nothing happens when page reloads
Remember that you can only save strings to localStorage, in a todo list you might want to save an array (a collection of todos), but since you can't do it you need to convert it to a string while saving (JSON.stringify(yourArray) will help you with that), and parse it back to an Array when loading (JSON.parse)
const form = document.querySelector('#add-task');
const input = document.querySelector('#todo');
const taskList = document.querySelector('#task-list');
let taskID = 0;
let tasks = [] // here will hold your current todos collection
// a function that will retrieve the saved todos from local storage
//
// note that 'tasks' can be any string identifier that you want — 'todos'
// would also work — but you need to use the same for localStorage.getItem
// and localStorage.setItem
function getTasksFromLocalStorage(){
// it will return `null` if nothing's there
tasks = localStorage.getItem('tasks') || []
if (tasks) {
// convert it to an array so you can loop over it
tasks = JSON.parse(tasks)
}
}
function addTask(text) {
// CREATE DOM ELEMENTS
const newTask = document.createElement('li');
const removeBtn = document.createElement('button');
removeBtn.innerText = 'Remove Task';
// set the text to the provided value
newTask.innerText = text;
// append the remove button
newTask.appendChild(removeBtn);
// append it to the dom so we can see it
taskList.appendChild(newTask)
}
// on page load get tasks from local storage
// then loop over it, create the DOM elements and append them to
// the taskList
document.addEventListener('DOMContentLoaded', function() {
getTasksFromLocalStorage()
// if we have saved tasks, loop over them and render to the dom
tasks.forEach(function(savedTaskText) {
addTask(savedTaskText)
})
})
// then on your code, you need to update to push
// the current inputed `task` to the `tasks` collection (Array)
// then save the entire collection to the local storage
// then add the new task to the DOM
// and finally reset the input
form.addEventListener('submit', function(e) {
e.preventDefault();
console.log(input.value);
// save it to the current holding list
tasks.push(input.value)
// save a copy of the updated list to the localStorage, so when you
// reload the page you get saved items!
localStorage.setItem('tasks', tasks)
// add it to DOM
addTask(input.value);
// reset the input
input.value = '';
});
There's more things you need to do, if you want tasks to have unique ids (since, so you can remove them later), but the code was simplified for brevity of explanation (and yet you got a long answer anyways).
Here's so docs and suggested reading:
MDN Docs for LocalStorage
MDN Docs for JSON (parse and stringify)
There's plenty vanilla javascript tutorials (written and youtube) for "creating a todo lists using localStorage", that go into more detail than we can go in a SO answer, I suggest you skim through those as well!
Good luck and Happy coding ✌️
There are 2 problems with your code.
First, you are not saving each to-do task entered by user upon form submit. If you want to save each to-do task entered by user in localStorage, then modify the form submit handler as below:
form.addEventListener('submit', function(e) {
e.preventDefault();
const newTask = document.createElement('li');
const removeBtn = document.createElement('button');
let savedInput = input.value;
removeBtn.innerText = 'Remove Task';
newTask.innerText = input.value;
newTask.appendChild(removeBtn);
taskList.appendChild(newTask);
localStorage.setItem('Task'+taskID, input.value);
taskID++;
input.value = '';
});
Second, you are not utilizing the previously saved data in localStorage to show the list of to-dos that were entered by user before the page was loaded. You can achieve that by using below function code:
function showSavedToDos() {
const keys = Object.keys(localStorage);
let i = keys.length;
while (i--) {
const newTask = document.createElement('li');
const removeBtn = document.createElement('button');
removeBtn.innerText = 'Remove Task';
newTask.innerText = localStorage.getItem(keys[i]);
newTask.appendChild(removeBtn);
taskList.appendChild(newTask);
}
}
showSavedToDos();
You are not using de localStorage API, please take a look to this example. here I am using template to display the tasks. In the html file is the only change
<main id="main">
<h1>THE TO-DO LIST:</h1>
<form action="" id="add-task">
<label for="todo">Add Task:</label>
<input type="text" id="todo" />
<button>Add Task</button>
</form>
<p class="center">To complete task, click on text.</p>
<ul id="task-list">
<li class="task-complete">example_1 <button>Remove Task</button></li>
</ul>
</main>
<template id="task">
<li class="task-complete">
<span></span>
<button>Remove task</button>
</li>
</template>
In JavaScript I create a render function that will collect the task stored in localstorage. Populated when calling store(input.value) in the submit handler
const form = document.querySelector("#add-task");
const input = document.querySelector("#todo");
const taskList = document.querySelector("#task-list");
let taskID = 0;
taskList.addEventListener("click", function (e) {
if (e.target.tagName === "BUTTON") {
e.target.parentElement.remove();
let inputTask = document.getElementById("todo");
localStorage.setItem("email", inputTask.value);
} else if (e.target.tagName === "LI") {
e.target.classList.toggle("task-complete");
}
});
form.addEventListener("submit", function (e) {
e.preventDefault();
console.log(input.value);
const newTask = document.createElement("li");
const removeBtn = document.createElement("button");
let savedInput = input.value;
removeBtn.innerText = "Remove Task";
newTask.innerText = input.value;
newTask.appendChild(removeBtn);
taskList.appendChild(newTask);
store(input.value);
input.value = "";
console.log(localStorage);
});
function getTasks() {
return localStorage.tasks ? JSON.parse(localStorage.tasks) : [];
}
function store(task) {
const tasks = getTasks();
tasks.push(task);
localStorage.setItem("tasks", JSON.stringify(tasks));
}
function render() {
const tasks = getTasks();
tasks.forEach((task) => {
const newTask = createTask(task);
taskList.appendChild(newTask);
});
}
function createTask(task) {
const template = document.querySelector("#task");
const taskNode = template.content.cloneNode(true);
taskNode.querySelector("span").innerText = task;
return taskNode;
}
render();
The render function run every first render of the page, so tasks list will be populated

How to fix Form action with method get

I have a small issues with my code.
Basically, I have a form in my index.html file:
The form from page 1 is the following:
<form method="get" name="basicSearch" id = "basicSearch" action="page2.html">
<input name="location" type="text" class="BasicSearch" id="searchInput" placeholder="Location">
<button type= "submit" class="BasicSearch" id="searchBtn" placeholder="Search"></button>
</form>
For this form, I want to use OpenWeatherMap API in order to get some weather data. My problem is the following:
I want to get what the user inputs in the form, which I think I can get by using, for example:
var searchInput = document.getElementById("searchInput");
In this variable I can store the location.
And this variable, I want to append to the link that does fetch the data from the API, in the javascript code.
When the user inputs, for example: New York, and press Search, the form action should redirect him to page2.html, where there I can show the weather data.
How can I show that weather data in the page2, with the location input from page1? I tried many times but no luck.
Some Javascript code down below:
let units = 'metric';
let searchMethod = 'q';
let searchButton = document.getElementById("searchBtn");
let searchInput = document.getElementById("searchInput");
if (searchButton) {
searchButton.addEventListener('click', () => {
let searchTerm = searchInput.value;
if (searchTerm)
searchWeather(searchTerm);
});
}
function searchWeather(searchTerm) {
fetch(`http://api.openweathermap.org/data/2.5/weather?${searchMethod}=${searchTerm}&APPID=${appId}&units=${units}`).then(result => {
return result.json();
}).then(result => {
init(result);
})
}
function init(resultFromServer){
let weatherDescriptionHeader = document.getElementById('weatherDescriptionHeader');
let temperatureElement = document.getElementById('temperature');
let humidityElement = document.getElementById('humidity');
let windSpeedElement = document.getElementById('windSpeed');
let cityHeader = document.getElementById('cityHeader');
let weatherIcon = document.getElementById('documentIconImg');
weatherIcon.src = 'http://openweathermap.org/img/w/' + resultFromServer.weather[0].icon + '.png';
let resultDescription = resultFromServer.weather[0].description;
weatherDescriptionHeader.innerText = resultDescription.charAt(0).toUpperCase() + resultDescription.slice(1);
temperatureElement.innerHTML = Math.floor(resultFromServer.main.temp) + '&#176' + " C";
windSpeedElement.innerHTML = 'Winds at ' + Math.floor(resultFromServer.wind.speed) + ' mph';
cityHeader.innerHTML = resultFromServer.name;
humidityElement.innerHTML = 'Humidity levels at ' + resultFromServer.main.humidity + '%';
}
That is some javascript code which should get the weather data.
Then, in page2, I have the following in HTML:
<div id = "weatherContainer">
<div id = "weatherDescription">
<h1 id = "cityHeader"></h1>
<div id= "weatherMain">
<div id = "temperature"></div>
<div id = "weatherDescriptionHeader"></div>
<div><img id = "documentIconImg"></div>
</div>
<hr>
<div id = "windSpeed" class = "bottom-details"></div>
<div id = "humidity" class = "bottom-details">></div>
</div>
</div>
I expected to have the weather data in page2, where the divs are.
Can somebody give me an advice, please?
Thank you!
Since the form in page1 doesn't exist in page 2, remove
let searchButton = document.getElementById("searchBtn");
let searchInput = document.getElementById("searchInput");
if (searchButton) {
searchButton.addEventListener('click', () => {
let searchTerm = searchInput.value;
if (searchTerm)
searchWeather(searchTerm);
});
}
instead put
ley searchTerm = new URLSearchParams(location.search).get('location');
searchWeather(searchTerm);
Explanation
When the page 1 form is submitted, it will load page2 like
page2.html?location=xxxx
where xxxx is the value of the <input name='location' ...
location.search will be ?location=xxxx
URLSearchParams makes dealing with these (when you have more than one especially) easier than the old method of splitting/decoding/jumping through hoops
We can simply just submit the form and get the current form input from url on page2.html
<form method="get" name="basicSearch" id = "basicSearch" action="page2.html">
<input name="location" type="text" class="BasicSearch" id="searchInput" placeholder="Location">
<button type= "submit" class="BasicSearch" id="searchBtn" placeholder="Search">Search</button>
</form>
And on the load of page2.html (before your ajax call), we can get the 'searchInput' (location) from URL by following:
<script>
let params = (new URL(document.location)).searchParams;
var searchInput= params.get('location');
</script>
Now, we can use 'searchInput' param for your api call and fetch the result.

Can't call jQuery function

I have a problem with my code.
I want to call a function but it's not working.
First, function show() displays button. This button has id='send' and it's inside the div with class='messagebox'. I want to call function on button click.
(I call function show in php script)
echo<<<ENDL
<div class="friendslistimgbox" onclick="show('$id','$login','$photo')">....</div>
ENDL;
$(.messagebox #send) or $(.messagebox > #send) are not working
$(document).ready(function(){
var conn = new WebSocket('ws://localhost:8080');
conn.onopen = function(e) {
console.log("Connection established!");
};
conn.onmessage = function(e) {
console.log(e.data);
var data = JSON.parse(e.data);
var row = data.from+": "+data.msg+"<br/>";
$("#chats").append(row);
};
$(".messagebox #send").click(function(){
var userId = $("#userId").val();
var msg = $("#msg").val();
var data = {
userId: userId,
msg: msg
};
conn.send(JSON.stringify(data));
})
})
function show(id,login,photo){
$('.messagebox').html("<input type='hidden' id='userId' value='"+login+"'><input type='text' id='msg' class='sendmessage'><button id='send' type='submit' class='button_sendmessage'><i class='icon-right-dir'></i></button>");
$('#message_to').html("<a href='"+login+"'><img src='../userphotos/"+photo+"'>"+login+"</a>");
$("#allmessagesbox").css("visibility","visible");
}
HTML /
<div class="allmessagesbox" id="allmessagesbox">
<div class="messages">
<div class="message_to" id="message_to"></div>
</div>
<div class="messagebox"></div>
</div>
<div id="chats"></div>
You'll need to use the .on() method to register events with DOM elements that are dynamic (ie like your button, which might exist in the future).
In the case of your code, you can use on() in the following way:
// Replace this line:
// $(".messagebox #send").click(function(){
// With this:
$("body").on("click", ".messagebox #send", function(){
var userId = $("#userId").val();
var msg = $("#msg").val();
var data = {
userId: userId,
msg: msg
};
conn.send(JSON.stringify(data));
})
This can basically be read and understood as:
// For any dynamic element in scope or child of the body
$("body")
// Register a click event with any element that matches the
// .messagebox #send selector either now, or in the future
.on("click", ".messagebox #send", function(){
...
}));
For more information on on(), see the jQuery documentation

Categories

Resources