on my website, there are 2 buttons. one button is to indent text to the left and the other button is to indent text to the right. what I am trying to achieve is that whenever I press the indent left or right button, currently selected (on focus) text should move left or right (depending on what button I press). at the same time, I want the font size of that text to go down by one level, so when compared to the text above, it's smaller to the text above.
// global variable
let onFocus = null;
// this function
function focusedText(event) {
onFocus = event.target;
}
function init() {
const newLineButton = document.querySelector('#newLine');
newLineButton.addEventListener("click", newLine);
newLine();
document.querySelector("#printButton").addEventListener("click", printPage)
}
function newLine() {
const newLine = document.createElement("p");
newLine.setAttribute("contenteditable", true);
newLine.addEventListener("keydown", handleKeyDown)
newLine.addEventListener("focus", focusedText)
newLine.classList.add("textLine")
const parent = document.querySelector('#textBox');
parent.appendChild(newLine);
newLine.focus();
}
function handleKeyDown(event) {
if (event.which === 13) {
event.preventDefault();
newLine();
}
}
// this runs the init(); function
init();
// this prints the page
function printPage() {
window.print();
}
// this fuction changes the text size
function textSize(selectTag) {
const listValue = selectTag.options[selectTag.selectedIndex].value;
onFocus.style.fontSize = listValue;
}
* {
padding-top: 0em;
padding-bottom: 0em;
margin-top: 0em;
margin-bottom: 0em;
}
*:focus {
outline: 0px solid transparent;
}
body {
margin: 0;
}
.title {
font-family: 'Assistant', sans-serif;
font-size: 2em;
text-align: center;
text-transform: uppercase;
color: white;
background: #6a0dad;
letter-spacing: 0.20em;
}
.navMenu {
display: flex;
align-items: stretch;
padding-top: 0.5em;
padding-bottom: 0.5em;
justify-content: center;
}
.navMenu button,
#dropDown {
padding: 0.3em 1.2em;
margin: 0 0.1em 0.1em 0;
border: 0.16em solid rgba(255, 255, 255, 0);
border-radius: 2em;
box-sizing: border-box;
font-family: 'Assistant', sans-serif;
font-weight: bold;
font-size: 1rem;
color: white;
text-shadow: 0 0.04em 0.04em rgba(0, 0, 0, 0.35);
text-align: center;
transition: all 0.2s;
background-color: #6a0dad;
cursor: pointer;
}
.navMenu button:hover,
#dropDown:hover {
border-color: white;
}
#dropDown option {
font-weight: bold;
}
#textBox {
display: flex;
justify-content: left;
width: auto;
flex-flow: column nowrap;
font-family: 'Assistant', sans-serif;
}
.textLine {
color: black;
width: 100%;
padding-left: 0.8em;
padding-right: 0.8em;
box-sizing: border-box;
transition: background-color 0.25s;
}
#media screen {
.textLine:hover {
background-color: #6a0dad;
color: white;
}
}
#media print {
.navMenu,
.title {
display: none;
}
}
.textLine:empty::before {
content: "Enter your text here";
color: grey;
font-family: 'Assistant', sans-serif;
pointer-events: none;
}
.textLine:hover:empty::before {
content: "Enter your text here";
color: white;
font-family: 'Assistant', sans-serif;
}
<!DOCTYPE html>
<html>
<head>
<title>Outline Editing</title>
<link rel="stylesheet" type="text/css" href="main-css.css">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body>
<main>
<h1 class="title">Outline Editing</h1>
<nav class="navMenu">
<button type="button" id="indentLeft"><i class="material-icons">format_indent_decrease</i></button>
<select id="dropDown" onchange="textSize(this);" size="1">
<option value="" selected disabled hidden>Levels</option>
<option value="200%">Level 1</option>
<option value="170%">Level 2</option>
<option value="150%">Level 3</option>
<option value="130%">Level 4</option>
<option value="100%">Level 5</option>
</select>
<button type="button" id="newLine">New Line</button>
<button type="button" id="indentRight"><i class="material-icons">format_indent_increase</i></button>
<button type="button" id="printButton">Print</button>
</nav>
<div id="textBox">
</div>
<script src="main.js"></script>
</main>
</body>
</html>
You could just add "Horizontal Tab" as an HTML entity on your focused text.
First change the <p> to <pre>.
function newLine() {
const newLine = document.createElement("pre");
//...
}
Update your init function:
function init() {
var newLineButton = document.querySelector('#newLine');
newLineButton.addEventListener("click", newLine);
newLine();
document.querySelector("#printButton").addEventListener("click", printPage);
let indentRight = document.getElementById("indentRight");
indentRight.addEventListener("click", IndentRight_Click);
let indentLeft = document.getElementById("indentLeft");
indentLeft.addEventListener("click", IndentLeft_Click);
}
Then you can add the "Horizontal Tab" on the text that is focused, checking if you have already added:
function IndentRight_Click(event) {
let horizontalTab = " ";
let originalHtml = onFocus.innerHTML;
if (!originalHtml.includes("\t")) onFocus.innerHTML = horizontalTab + originalHtml;
onFocus.focus();
}
And finally, check for it again on your "Left Indentation":
function IndentLeft_Click(event) {
var originalHtml = onFocus.innerHTML;
if (originalHtml.includes("\t")) onFocus.innerHTML = originalHtml.replace("\t", "");
onFocus.focus();
}
Related
I need to get a list of all files by iterating all folders in the specified parent folder.
I have heard that recursion is the best way to achieve, but I am unsure how to use recursion for fetching list of files in a folder using Dropbox API.
The below code works well for fetching the files in a single folder, I am looking to modify the code so it does a recursive fetch
<!doctype html>
<html>
<head>
<title>Dropbox JavaScript SDK</title>
<style>
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
line-height: 1.5;
}
.container {
display: block;
width: 90%;
max-width: 800px;
margin-left: auto;
margin-right: auto;
}
.container.main {
padding-top: 30px;
}
code, .code {
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
color: #666;
}
.info {
font-size: 13px;
font-style: italic;
color: #666;
margin-top: 40px;
}
a {
color: #007ee5;
}
input {
border: 2px solid #007ee5;
border-radius: 3px;
padding: 8px;
font-size: 16px;
}
.button, button {
border-radius: 3px;
background-color: #007ee5;
border: none;
color: #fff;
font-size: 16px;
padding: 10px 15px;
text-decoration: none;
}
.page-header {
background-color: #007ee5;
padding: 10px 0 0 0;
}
.page-header .container {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 150px;
}
.page-header a {
color: #fff;
text-decoration: none;
}
.page-header nav {
display: flex;
justify-content: space-between;
align-items: center;
}
.page-header h1 {
display: flex;
align-items: center;
color: #fff;
font-size: 17px;
font-weight: 200;
}
.page-header .logo {
width: 100px;
margin-right: 10px;
}
.page-header .view-source {
font-weight: 200;
font-size: 12px;
}
.page-header h2 {
color: #fff;
font-size: 18px;
font-weight: normal;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/promise-polyfill#7/dist/polyfill.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/2.0.3/fetch.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dropbox.js/10.27.0/Dropbox-sdk.min.js" integrity="sha512-nTLJySi/DUYzRvvxWOxf31QS5191sN1gpoq6EqGFHPFH0RlM6xOiY6jEp9dmwhDlyFmCmicwLOMnE+fUmo02KQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>
<body>
<!-- Example layout boilerplate -->
<header class="page-header">
<div class="container">
<nav>
<a href="/">
<h1>
<img src="https://cfl.dropboxstatic.com/static/images/brand/logotype_white-vflRG5Zd8.svg" class="logo" />
JavaScript SDK Examples
</h1>
</a>
View Source
</nav>
<h2 class="code">
examples / basic
</h2>
</div>
</header>
<!-- Example description and UI -->
<section class="container main">
<p>This example fetches the contents of your root Dropbox directory. It uses the <code>Dropbox.filesListFolder()</code> method [docs].</p>
<form id="basic-form" onSubmit="return listFiles()">
<input type="text" id="access-token" placeholder="Access token" />
<button type="submit">Submit</button>
</form>
<!-- The files returned from the SDK will be added here -->
<ul id="files"></ul>
<p class="info">To obtain an access token for quick testing, you can go to API Explorer click the "Get Token" button on the top right, copy the token it creates and then paste it here.</p>
</section>
<!-- Scripts to run example -->
<script>
var form = document.getElementById('basic-form');
form.onsubmit = function listFiles(e) {
e.preventDefault();
var ACCESS_TOKEN = document.getElementById('access-token').value;
var dbx = new Dropbox.Dropbox({ accessToken: ACCESS_TOKEN });
dbx.filesListFolder({path: ''})
.then(function(response) {
console.log('response', response)
displayFiles(response.result.entries);
})
.catch(function(error) {
console.error(error);
});
}
function displayFiles(files) {
var filesList = document.getElementById('files');
var li;
for (var i = 0; i < files.length; i++) {
li = document.createElement('li');
li.appendChild(document.createTextNode(files[i][".tag"] + " " + files[i].name));
filesList.appendChild(li);
}
}
</script>
</body>
</html>
Quick attempt, non-tested code but you can use it as inspiration:
form.onsubmit = function listFiles(e) {
e.preventDefault();
var ACCESS_TOKEN = document.getElementById('access-token').value;
var dbx = new Dropbox.Dropbox({ accessToken: ACCESS_TOKEN });
var entries = {};
let fetch_files = function(_path, entries){
await dbx.filesListFolder({path: _path})
.then(function(response) {
console.log('response', response);
entries = response.result.entries;
for (var i = 0; i < entries.length; i++) {
if(entries[i]['.tag'] == 'folder'){
entries[i]['subentries'] = {};
fetch_files(entries[i]['path_display'], entries[i]['subentries']);
}
}
})
.catch(function(error) {
console.error(error);
});
};
fetch_files('', entries);
if(Object.keys(entries).length !== 0){
displayFiles(entries);
}
}
function displayFiles(files) {
var filesList = document.getElementById('files');
let append_list = function(arr, recursion){
var li;
for (var i = 0; i < arr.length; i++) {
li = document.createElement('li');
li.appendChild(document.createTextNode(" ".repeat(recursion * 4) + arr[i][".tag"] + " " + arr[i].name));
filesList.appendChild(li);
if('subentries' in arr[i]){
append_list(arr[i]['subentries'], recursion + 1);
}
}
}
append_list(files, 0)
}
I am making a todo page and want to count the number of todos and display them.
<div class='numtodos'>3</div>
I want to display them here in the text content of numtodos.
Trying to count the number of todo lis using jQuery (I could also use a non-jQuery answer).
I know I would need the length of <li> elements, but how should I save it to update when a new todo is added.
Here is my code pen. trying to make the large number on the right be the number of todos.
$('ul').on('click', 'li', function() {
$(this).toggleClass('completed');
})
$('ul').on('click', 'span', function(event) {
$(this).parent().fadeOut(500, function() {
$(this).remove();
})
event.stopPropagation();
});
$("input[type='text']").keypress(function(event) {
if (event.which === 13) {
//get the new todo text from inut
var todoText = $(this).val();
$(this).val('');
//create a new li and add it ul
$('ul').append('<li><i class="far fa-circle"></i> ' + todoText + ' <span>X</span></li>')
}
});
$(".fa-plus-circle").click(function() {
$("input[type='text']").fadeToggle();
});
// let lis = document.querySelectorAll('li');
// let input = document.querySelector('input');
// let numtodos = document.querySelector('.numtodos')
// input.addEventListener('change', function(){
// let howmany = lis.length;
// numtodos.textContent= howmany;
// })
$('input').change(function() {
console.log($('.numtodos'));
})
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: rgb(67, 0, 192);
font-family: 'Roboto', sans-serif;
}
.container {
background-color: whitesmoke;
width: 480px;
height: 100%;
padding: 35px 40px;
}
/* NAV */
#nav {
display: flex;
justify-content: space-between;
align-items: center;
}
.tinyburger {
cursor: pointer;
}
.line {
height: 3px;
width: 21px;
background: black;
margin: 3px;
border-radius: 1px;
}
/* header */
.header {
display: flex;
margin: 40px 0px 30px;
font-family: 'Roboto Condensed', sans-serif;
}
h1 {
margin-right: 8px;
font-size: 3em;
}
h2 {
font-size: 1.4em;
margin-top: 26px;
color: rgb(153, 153, 153);
}
/* Input */
input {
width: 100%;
padding: 10px 5px;
border: none;
font-size: 17px;
}
ul {
list-style: none;
}
li {
line-height: 1.2;
border-bottom: 1px rgb(202, 202, 202) solid;
margin: 20px 5px;
padding: 0px 0 20px 0;
font-weight: bold;
cursor: pointer;
}
li span {
float: right;
}
.fa-plus-circle {
color: rgb(67, 0, 192);
font-size: 40px;
text-align: center;
width: 100%;
margin-top: 30px;
}
.numtodos {
font-size: 12em;
position: absolute;
top: 0%;
left: 75%;
line-height: 0.7;
color: rgba(153, 153, 153, 0.2);
font-family: 'Roboto Condensed', sans-serif;
}
.fa-circle {
color: rgba(153, 153, 153, 0.4);
font-size: 20px;
}
.completed {
text-decoration: line-through;
color: grey;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css" integrity="sha512-1PKOgIY59xJ8Co8+NE6FZ+LOAZKjy+KY8iq0G4B3CyeY6wYHN3yt9PW0XpSriVlkMXe40PTKnXrLnZ9+fkDaog==" crossorigin="anonymous" />
<link href="https://fonts.googleapis.com/css2?family=Roboto+Condensed:ital,wght#0,400;1,700&family=Roboto:wght#400;500;700;900&display=swap" rel="stylesheet">
</head>
<body>
<div class="container">
<div id='nav'>
<div class='tinyburger'>
<div class='line'></div>
<div class='line'></div>
<div class='line'></div>
</div>
<i class="fas fa-search"></i>
</div>
<div class="header">
<h1>TO-DO</h1>
<h2 class="date">NOV 23</h2>
</div>
<input type="text" name='newtodo' id='newtodo' placeholder="Write a new to-do">
<ul class='todos'>
<li><i class="far fa-circle"></i> Lunch with Helena Barnes <span>X</span> </li>
<li><i class="far fa-circle"></i> Evening Workout<span>X</span></li>
<li><i class="far fa-circle"></i> Lunch with Helena Barnes <span>X</span></li>
</ul>
<i class="fas fa-plus-circle"></i>
<div class='numtodos'>3</div>
</div>
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
https://codepen.io/laurynatlanta/pen/QWNxYeN
I think this should be fine:
// Get todos number container div element.
var numTodosContainer = document.getElementsByClassName('numtodos')[0];
// Get all todos available.
var todos = document.getElementsByClassName(todosclassname);
// Get todos length to add it to the container.
var lastTodosNum = todos.length;
// Add it to the container.
numTodosContainer.innerHTML = lastTodosNum;
// Loop to check every 10ms if the number of todos is changed.
setInterval(function(){
// This variable will change every 10ms to get the new todos number.
var newTodosNum = document.getElementsByClassName(todosclassname).length;
// Check if the new number isn't equals the old num.
if (newTodosNum != lastTodosNum) {
numTodosContainer.innerHTML = newTodosNum;
}
// Make lastTodosNum equals newTodosNum to stop the if statement above from execute the code block inside it.
lastTodosNum = newTodosNum;
}, 10);
you have todo something like that:
const numtodos = document.querySelector('div.numtodos')
numtodos.setCount =_=> {
numtodos.textContent = document.querySelectorAll('ul.todos>li:not(.completed)').length
}
then just add numtodos.setCount(); on rights places.
so, you JS become:
const numtodos = document.querySelector('div.numtodos')
numtodos.setCount =_=> {
numtodos.textContent = document.querySelectorAll('ul.todos>li:not(.completed)').length
}
$('ul').on('click', 'li', function () {
$(this).toggleClass('completed');
numtodos.setCount();
});
$('ul').on('click', 'span', function (event) {
$(this).parent().fadeOut(500, function () {
$(this).remove();
numtodos.setCount();
});
event.stopPropagation();
});
$("input[type='text']").keypress(function (event) {
if (event.which === 13) {
//get the new todo text from inut
var todoText = $(this).val();
$(this).val('');
//create a new li and add it ul
$('ul').append(`<li><i class="far fa-circle"></i>${todoText}<span>X</span></li>`);
numtodos.setCount();
}
});
$(".fa-plus-circle").click(function () {
$("input[type='text']").fadeToggle();
});
Based on your current code, I'd suggest creating the following named function, and then calling that function in the areas of your code where you're updating the the to-do list (whether adding to, or removing from, it):
// we're not planning to change the function within the code, so we
// declare using 'const' (though 'let' would be perfectly okay, but would
// potentially allow for the variable to be overwritten); we also use
// Arrow function syntax since - within the function - we have no need
// to use 'this':
const todosLength = () => {
// here we find the number of <li> elements that do not have
// the class of 'completed' that are the children of a parent
// with the class of 'todos'; and then we retrieve the length
// of the resulting jQuery collection:
const num = $('.todos > li:not(.completed)').length;
// here we set the text of the element(s) with the
// class of 'numtodos':
$('.numtodos').text(num);
// here we return the number to the calling context
// (just in case it's ever of use):
return num;
};
When the above is merged with your code it yields the following:
const todosLength = () => {
const num = $('.todos li:not(.completed)').length;
$('.numtodos').text(num);
return num;
}
$('ul').on('click', 'li', function() {
$(this).toggleClass('completed');
})
$('ul').on('click', 'span', function(event) {
$(this).parent().fadeOut(500, function() {
$(this).remove();
todosLength();
});
event.stopPropagation();
});
$("input[type='text']").keypress(function(event) {
if (event.which === 13) {
//get the new todo text from inut
var todoText = $(this).val();
$(this).val('');
//create a new li and add it ul
$('ul').append('<li><i class="far fa-circle"></i> ' + todoText + ' <span>X</span></li>');
todosLength();
}
});
$(".fa-plus-circle").click(function() {
$("input[type='text']").fadeToggle();
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: rgb(67, 0, 192);
font-family: 'Roboto', sans-serif;
}
.container {
background-color: whitesmoke;
width: 480px;
height: 100%;
padding: 35px 40px;
}
/* NAV */
#nav {
display: flex;
justify-content: space-between;
align-items: center;
}
.tinyburger {
cursor: pointer;
}
.line {
height: 3px;
width: 21px;
background: black;
margin: 3px;
border-radius: 1px;
}
/* header */
.header {
display: flex;
margin: 40px 0px 30px;
font-family: 'Roboto Condensed', sans-serif;
}
h1 {
margin-right: 8px;
font-size: 3em;
}
h2 {
font-size: 1.4em;
margin-top: 26px;
color: rgb(153, 153, 153);
}
/* Input */
input {
width: 100%;
padding: 10px 5px;
border: none;
font-size: 17px;
}
ul {
list-style: none;
}
li {
line-height: 1.2;
border-bottom: 1px rgb(202, 202, 202) solid;
margin: 20px 5px;
padding: 0px 0 20px 0;
font-weight: bold;
cursor: pointer;
}
li span {
float: right;
}
.fa-plus-circle {
color: rgb(67, 0, 192);
font-size: 40px;
text-align: center;
width: 100%;
margin-top: 30px;
}
.numtodos {
font-size: 12em;
position: absolute;
top: 0%;
left: 75%;
line-height: 0.7;
color: rgba(153, 153, 153, 0.2);
font-family: 'Roboto Condensed', sans-serif;
}
.fa-circle {
color: rgba(153, 153, 153, 0.4);
font-size: 20px;
}
.completed {
text-decoration: line-through;
color: grey;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css" integrity="sha512-1PKOgIY59xJ8Co8+NE6FZ+LOAZKjy+KY8iq0G4B3CyeY6wYHN3yt9PW0XpSriVlkMXe40PTKnXrLnZ9+fkDaog==" crossorigin="anonymous" />
<link href="https://fonts.googleapis.com/css2?family=Roboto+Condensed:ital,wght#0,400;1,700&family=Roboto:wght#400;500;700;900&display=swap" rel="stylesheet">
<div class="container">
<div id='nav'>
<div class='tinyburger'>
<div class='line'></div>
<div class='line'></div>
<div class='line'></div>
</div>
<i class="fas fa-search"></i>
</div>
<div class="header">
<h1>TO-DO</h1>
<h2 class="date">NOV 23</h2>
</div>
<input type="text" name='newtodo' id='newtodo' placeholder="Write a new to-do">
<ul class='todos'>
<li><i class="far fa-circle"></i> Lunch with Helena Barnes <span>X</span> </li>
<li><i class="far fa-circle"></i> Evening Workout<span>X</span></li>
<li><i class="far fa-circle"></i> Lunch with Helena Barnes <span>X</span></li>
</ul>
<i class="fas fa-plus-circle"></i>
<div class='numtodos'>3</div>
</div>
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
JS Fiddle demo.
References:
CSS:
Negation (:not()) pseudo-class.
jQuery:
text().
I'm trying to create a GPA calculator (just to practice html, css, javascript) and am having an issue I can't seem to solve. Here's what's happening.
TL;DR: When I click on an input field, the corresponding object is erased.
For each course, there is a form with input fields for course name (text input), grade (select input), and credits earned (number input). When you want to add another course, you're adding another form. When a form is created, so is an object that's associated with it. I create this association by adding an id to the form with a unique number and also adding that id number to the object.
When I console.log the various steps of creating an object and setting its values and saving it to the local storage, everything works fine. However, when I was trying to create functionality to update the values of the objects, I started having troubles. I've pinpointed when they happen. Whenever I click on an input field, the object is erased. Specifically, only the object that's tied to that input field is erased. So if I create 5 forms and I click on the text input of the 3rd one, the 3rd object is erased. I can click on that same field multiple times and nothing else will happen. Then, if I click, say, the number input of the 5th form, then the 5th object is erased. I know it's not the delete function at work because that function would also remove the form from view.
If anyone could help me pinpoint what I can do to fix this, I would greatly appreciate it.
Javascript
// Theme
let lightMode = document.getElementById('light-mode');
let darkMode = document.getElementById('dark-mode');
lightMode.addEventListener('click', () => {themeTransition(); document.documentElement.setAttribute('data-theme', 'light');});
darkMode.addEventListener('click', () => {themeTransition(); document.documentElement.setAttribute('data-theme', 'dark');});
function themeTransition()
{
document.documentElement.classList.add('transition');
window.setTimeout(() => document.documentElement.classList.remove('transition'), 1000);
}
// GPA CALCULATION
// Object ID
var objId = 0;
// Grades class
class Grades
{
constructor(id, course, grade, credits)
{
this.id = id;
this.course = course;
this.grade = grade;
this.credits = credits;
}
}
// Storage class
class Store
{
static getGrades()
{
let grades;
if (localStorage.getItem('grades') == null)
{
grades = [];
}
else
{
grades = JSON.parse(localStorage.getItem('grades'));
objId = localStorage.getItem('objId');
}
return grades;
}
static addGrade(grade, newObjId)
{
const grades = Store.getGrades();
grades.push(grade);
localStorage.setItem('grades', JSON.stringify(grades));
localStorage.setItem('objId', JSON.stringify(newObjId));
console.log(grades);
}
static updateGrade(id, course, grade, credits)
{
const grades = Store.getGrades();
grades.forEach((oldGrade, index) =>
{
if (oldGrade.id == id)
{
oldGrade[index].course = course;
oldGrade[index].grade = grade;
oldGrade[index].credits = credits;
}
});
localStorage.setItem('grades', JSON.stringify(grades));
}
static removeGrade(id)
{
const grades = Store.getGrades();
grades.forEach((grade, index) =>
{
if (grade.id == id)
{
grades.splice(index, 1);
}
});
localStorage.setItem('grades', JSON.stringify(grades));
}
}
// UI class
class UI
{
static displayGrades()
{
const grades = Store.getGrades();
grades.forEach((grade) => UI.addCurrentGrade(grade));
}
static addCurrentGrade(grade)
{
const list = document.querySelector('.add-grades');
const row = document.createElement('form');
const addBefore = document.querySelector('.add');
row.classList.add('add-item');
row.setAttribute('id', `${grade.id}`)
row.innerHTML =
`
<div class="input-node course">
<label for="course">Course</label>
<input type="text" id="course">
</div>
<div class="input-node grade">
<label for="grade">Grade</label>
<select id="grade">
<option value="none"></option>
<option value="A">A</option>
<option value="A-">A-</option>
<option value="B+">B+</option>
<option value="B">B</option>
<option value="B-">B-</option>
<option value="C+">C+</option>
<option value="C">C</option>
<option value="C-">C-</option>
<option value="D+">D+</option>
<option value="D">D</option>
<option value="D-">D-</option>
<option value="F">F</option>
<option value="W">W</option>
<option value="I">I</option>
<option value="P">P</option>
<option value="NP">NP</option>
</select>
</div>
<div class="input-node credits">
<label for="credits">Credits</label>
<input type="number" id="credits">
</div>
<div class="input-node delete">
<i class="fas fa-times-circle"></i>
</div>
`;
list.insertBefore(row, addBefore);
document.querySelector('.delete').addEventListener('click', delGrade);
document.querySelector('.add-grades').addEventListener('input', updateGrades);
}
static addNewGrade(e)
{
if (e.parentElement.classList.contains('add-grade'))
{
objId++;
const currentId = objId;
const list = document.querySelector('.add-grades');
const row = document.createElement('form');
const addBefore = document.querySelector('.add');
row.classList.add('add-item');
row.setAttribute('id', `${currentId}`)
row.innerHTML =
`
<div class="input-node course">
<label for="course">Course</label>
<input type="text" id="course">
</div>
<div class="input-node grade">
<label for="grade">Grade</label>
<select id="grade">
<option value="none"></option>
<option value="A">A</option>
<option value="A-">A-</option>
<option value="B+">B+</option>
<option value="B">B</option>
<option value="B-">B-</option>
<option value="C+">C+</option>
<option value="C">C</option>
<option value="C-">C-</option>
<option value="D+">D+</option>
<option value="D">D</option>
<option value="D-">D-</option>
<option value="F">F</option>
<option value="W">W</option>
<option value="I">I</option>
<option value="P">P</option>
<option value="NP">NP</option>
</select>
</div>
<div class="input-node credits">
<label for="credits">Credits</label>
<input type="number" id="credits">
</div>
<div class="input-node delete">
<i class="fas fa-times-circle"></i>
</div>
`;
list.insertBefore(row, addBefore);
document.querySelector('.delete').addEventListener('click', delGrade);
document.querySelector('.add-grades').addEventListener('input', updateGrades);
const course = e.parentElement.parentElement.querySelector('#course').value;
const grade = e.parentElement.parentElement.querySelector('#grade').value;
const credits = e.parentElement.parentElement.querySelector('#course').value;
const grades = new Grades(currentId, course, grade, credits);
Store.addGrade(grades, currentId);
}
}
static deleteGrade(del)
{
if (del.parentElement.classList.contains('delete'))
{
del.parentElement.parentElement.remove();
}
}
}
// EVENTS
// Display Existing Grades
document.addEventListener('DOMContentLoaded', UI.displayGrades);
// Add Grade
document.querySelector('.add-grade').addEventListener('click', (e) =>
{
UI.addNewGrade(e.target);
});
// Update Grade
document.querySelector('.add-grades').addEventListener('input', updateGrades);
function updateGrades(e)
{
const id = e.target.parentElement.parentElement.getAttribute('id');
const course = e.target.parentElement.parentElement.querySelector('#course').value;
const grade = e.target.parentElement.parentElement.querySelector('#grade').value;
const credits = e.target.parentElement.parentElement.querySelector('#course').value;
Store.updateGrade(id, course, grade, credits);
}
// Remove Grade
document.querySelector('.add-grades').addEventListener('click', delGrade);
function delGrade(e)
{
UI.deleteGrade(e.target);
Store.removeGrade(e.target.parentElement.parentElement.getAttribute('id'));
}
document.body.addEventListener('click', testFun);
function testFun()
{
const grades = Store.getGrades();
console.log(grades);
}
note: function testFun() is to log the objects in the Grades class whenever I click on anything
HTML
<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.13.1/css/all.css" integrity="sha384-xxzQGERXS00kBmZW/6qxqJPyxW3UR0BPsL4c8ILaIWXva5kFi7TxkIIaMiKtqV1Q" crossorigin="anonymous">
<link rel="stylesheet" href="style.css">
<title>GPA Calculator</title>
</head>
<body>
<div class="container">
<div id="view-mode">
<button id="light-mode">Light</button> | <button id="dark-mode">Dark</button>
</div>
<header class="header">
<h1>GPA</h1>
<h2>Calculator</h2>
</header>
<section class="add-grades">
<div class="table-header">
<h1>Semester <span class="semester">–</span></h1>
<h2>Semester GPA: <span class="gpa-semester">–</span></h2>
</div>
<div class="add add-grade">
<i class="fas fa-plus-circle"></i>
</div>
</section>
<div class="add add-semester">
<i class="fas fa-plus-circle"></i>
</div>
<aside class="gpa-total">
<h2>GPA</h2>
<h1><span class="gpa-total">–</span></h1>
</aside>
</div>
<script src="main.js"></script>
</body>
</html>
If you want the full effect, here's the CSS as well
/* Global */
html[data-theme="light"]
{
--background: rgb(235, 235, 235);
--panel: rgb(245, 245, 245);
--panel-head: rgb(0, 80, 190);
--text: rgb(20, 20, 20);
--text-secondary: rgb(60, 60, 60);
--text-panel: rgb(20, 20, 20);
--text-panel-head: rgb(245, 245, 245);
--add: rgb(58, 180, 34);
--delete: rgb(255, 55, 55);
}
html[data-theme="dark"]
{
--background: rgb(40, 40, 40);
--panel: rgb(70, 70, 70);
--panel-head: rgb(0, 106, 255);
--text: rgb(245, 245, 245);
--text-secondary: rgb(120, 120, 120);
--text-panel: rgb(245, 245, 245);
--text-panel-head: rgb(245, 245, 245);
--add: rgb(58, 180, 34);
--delete: rgb(255, 55, 55);
}
*
{
margin: 0;
padding: 0;
box-sizing: border-box;
}
body
{
background: var(--background);
font-family: Cochin, Georgia, Times, 'Times New Roman', serif;
font-size: 16px;
color: var(--text);
}
.container
{
width: 80%;
margin: auto;
}
button
{
background: none;
border: none;
outline: none;
}
/* Header */
#view-mode
{
text-align: right;
margin-top: 20px;
}
#view-mode button
{
font-size: 16px;
color: var(--text);
}
#view-mode button:hover
{
color: var(--panel-head);
text-decoration: underline;
cursor: pointer;
}
.header
{
margin: 20px 0 50px 0;
text-align: center;
text-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
text-transform: uppercase;
}
.header h1
{
font-size: 6em;
}
.header h2
{
font-size: 1.77em;
color: var(--text-secondary);
}
/* User Input */
.add-grades
{
min-width: 400px;
max-width: 500px;
margin: auto;
margin-top: 20px;
background: var(--panel);
color: var(--text-panel);
border-radius: 20px;
display: flex;
flex-direction: column;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
}
.table-header
{
padding: 10px 15px;
display: flex;
justify-content: space-between;
align-items: baseline;
border-top-right-radius: 20px;
border-top-left-radius: 20px;
background: var(--panel-head);
color: var(--text-panel-head);
}
.table-header h1
{
font-size: 2em;
}
.table-header h2
{
font-size: 1.2em;
}
.add-item
{
padding: 10px;
display: flex;
justify-content: space-between;
align-items: center;
}
.add-item .input-node
{
display: flex;
justify-content: space-between;
align-items: center;
}
.add-item .input-node *
{
margin: 0 3px;
}
.input-node #course
{
width: 75px;
padding: 5px;
border: none;
border-radius: 5px;
outline: none;
box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.5);
}
.input-node #grade
{
font-size: 1em;
outline: none;
}
.input-node #credits
{
width: 40px;
padding: 5px;
border: none;
border-radius: 5px;
outline: none;
box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.5);
}
.delete i
{
font-size: 1.4em;
padding: 0;
color: var(--text-panel);
}
.delete i:hover
{
color: var(--delete);
cursor: pointer;
}
.add-grades .add
{
margin: 13px;
margin-top: 10;
display: flex;
justify-content: flex-end;
}
.add i
{
font-size: 1.4em;
color: var(--text-panel);
}
.add i:hover
{
color: var(--panel-head);
cursor: pointer;
}
.container > .add
{
min-width: 400px;
max-width: 500px;
margin: auto;
margin-top: 20px;
}
/* GPA Calculation */
.gpa-total
{
margin-top: 50px;
text-align: center;
}
.gpa-total h2
{
font-size: 3em;
text-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
}
.gpa-total h1
{
width: 250px;
margin: auto;
margin-top: 10px;
padding: 60px 0;
font-size: 6em;
border-radius: 100%;
background: var(--panel-head);
color: var(--text-panel-head);
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
}
/* Transition */
html.transition,
html.transition body,
html.transition body:before,
html.transition body:after,
html.transition section,
html.transition section:before,
html.transition section:after
{
transition: all 750ms !important;
transition-delay: 0 !important;
}
I am creating textarea tags as the user clicks a button. And i want the dynamically created texarea tags to remain as such when we close and open the browser again.
I am able to save the CONTENT of the textarea tag,but there is no point in it when the textarea tag itself doesnt remain after closing the browser.
ok: SO here is the code :
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<button id="A" onclick="add()" type="button">ADD</button>
<button id="S" onclick="save()" type="button">SAVE</button>
<button id="E" onclick="edit()" type="button">EDIT</button>
<button id="D" onclick="del('x')" type="button">DELETE</button>
</body>
<script type="text/javascript">
var text_new,x;
var i=0,j,y;
function add()
{
text_new=document.createElement("textarea");/*I WANT TO STORE THESE CREATED TAGS USING LOCAL STORAGE*/
text_new.id="a"+i.toString();
var t = document.createTextNode("");
text_new.appendChild(t);
console.log(text_new.id);
i++;
document.body.appendChild(text_new);
}
document.body.addEventListener("click", activate);
function activate()
{
if(document.activeElement.tagName.toLowerCase() ==="textarea")
{
x = document.activeElement.id;
y=x;
console.log(x);
console.log(typeof x);
}
}
function save()
{
document.getElementById(x).readOnly=true;
console.log(document.getElementById(x).value);
localStorage.y=document.getElementById(x).value;
document.getElementById(x).value=localStorage.y;
}
function edit()
{
document.getElementById(x).readOnly=false;
}
function del()
{
var element = document.getElementById(x);
element.remove();
}
</script>
</html>
Suggest you to try this.
Cookies are data, stored in small text files, on your computer.
When a user visits a web page, his name can be stored in a cookie.
Next time the user visits the page, the cookie "remembers" his name.
https://www.w3schools.com/js/js_cookies.asp
You can use html5 web storage, specifically the localStorage.
https://www.w3schools.com/html/html5_webstorage.asp
I hope this Helps!
ok i got it....
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<style type="text/css">
body
{
box-sizing: border-box;
background-image: url(images/note2.jpg);
/* Full height */
height: 100%;
/* Center and scale the image nicely */
background-position: center;
background-repeat: no-repeat;
background-size: cover;
background-attachment: fixed;
}
body, html {
height: 100%;
margin: 0;
}
button {
display: inline-block;
width: 150px;
background: black;
margin: 0 10px 0 0;
color: white;
font-size: 25px;
font-family: Oswald, Helvetica, Arial, sans-serif;
line-height: 1.8;
appearance: none;
box-shadow: none;
text-align: center;
border-radius: 20px;
border : 6px solid black;
}
#D:hover
{
background: red;
}
#S:hover
{
background: green;
}
button:hover
{
background-color: #417cb8
}
button:active
{
background-color: #417cb8;
box-shadow: 0 5px #27496d;
transform: translateY(5px);
}
textarea
{
height: 170px;
width: 500px;
border: 3px solid black;
border-radius: 20px;
resize: none;
font-family: Tahoma, Verdana, Segoe, sans-serif;
font-size: 20px;
padding: 10px;
letter-spacing: 2px;
opacity: 0.6;
text-overflow: auto;
}
#header
{
height: 100px;
font-family: Georgia, Times, "Times New Roman", serif;
font-size: 40px;
text-align: center;
padding-top: 30px;
position: relative;
}
#buts
{
position: relative;
margin: 0 auto;
}
#con
{
position: relative;
text-align: center;
padding: 10px;
}
img
{
position: absolute;
height: 60%;
}
</style>
<body>
<div id="header">NOTE IT OR FORGET IT!
<img src="images/note1.png"> </div>
<div id="con">
<div id="buts">
<button id="A" onclick="add()" type="button">ADD</button>
<button id="S" onclick="save()" type="button">SAVE</button>
<button id="E" onclick="edit()" type="button">EDIT</button>
<button id="D" onclick="del('x')" type="button">DELETE</button>
</div>
</div>
</body>
<script type="text/javascript">
var text_new,x;
var i=0,j,y,num=0;
window.onload=function ()
{i=0;
for (var key in localStorage)
{
text_new=document.createElement("textarea");
var t = document.createTextNode(localStorage.getItem(key));
text_new.appendChild(t);
document.body.appendChild(text_new);
text_new.id=key;
i++;
}
}
/*window.onbeforeunload=function()
{
var x=document.querySelectorAll("textarea");
for(num=0;num<x.length;x++)
{
if
}
}
}*/
function add()
{
text_new=document.createElement("textarea");
text_new.id="a"+i.toString();
for(var key in localStorage)
{
if (text_new.id==key)
{
i++;
text_new.id="a"+i.toString();
}
}
var t = document.createTextNode("");
text_new.appendChild(t);
console.log(text_new.id);
i++;
document.body.appendChild(text_new);
}
document.body.addEventListener("click", activate);
function activate()
{
if(document.activeElement.tagName.toLowerCase() ==="textarea")
{
x = document.activeElement.id;
console.log(x);
}
}
function save()
{
if((document.getElementById(x).readOnly==false)&&(document.getElementById(x).value!=""))
{
document.getElementById(x).readOnly=true;
console.log(x);
console.log(document.getElementById(x).value);
localStorage.setItem(x,document.getElementById(x).value);
document.getElementById(x).value=localStorage.getItem(x);
}
}
function edit()
{
document.getElementById(x).readOnly=false;
}
function del()
{
var element = document.getElementById(x);
localStorage.removeItem(x);
element.remove();
}
</script>
</html>
I use for a project Font Awesome for icons and because they do not show in IE8 when I use it like:
<i class="fa fa-bars fa-2x"></i>
I am forced to use the cheat sheet icons as this:
<i class="mobile-search-icon fa fa-lg"></i>
This way, the icons show OK in IE8 as well.
Now, the problem... Using the cheat sheet icons, how can I replace the value with another on toggle? Please check the example below where you see initially the search icon and on click I should see a close icon and on another click, show again the search icon.
The example below is the closest way I have found so far, and I need some help if possible. Thanks
$(".mobile-search-icon").on('click', function(e) {
$(this).siblings(".search-container").fadeToggle(500);
if ($.trim($(this).text()) === '') {
$(this).text('');
} else {
$(this).text('');
}
return false;
});
.search-bar {
display: table-cell;
vertical-align: middle;
}
.mobile-search-icon {
color: #fff;
display: block;
line-height: 50px;
width: 150px;
z-index: 1;
background-color: black;
text-align: center;
padding: 20px;
}
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<div class="search-bar">
<i class="mobile-search-icon fa fa-lg"></i>
</div>
Not sure exactly what you want, but it looks neat. I made a demo that uses FA's Unicode on Snippet 1.
I re-read your question, I think I understand ... You just want to toggle the button between 2 icons. If so look at Snippet 2. Your problem is that you are using .text() method which will add non-parsed plain text. .html() will take text and parse it to HTML so it's rendered as what the text represents, and not what it is literally.
SNIPPET 1
var tgt = document.querySelector('.search-bar');
var btn = document.querySelector('.search-btn');
btn.addEventListener('click', function(e) {
var res = document.getElementById('result');
var txt = document.getElementById('inputText').value;
var mom = res.parentNode;
if (res) {
mom.removeChild(res);
}
genFA(txt);
}, false);
function genFA(x) {
var baseUni = "";
var iEle = document.createElement('i');
iEle.id = 'result';
iEle.classList.add('fa');
iEle.classList.add('fa-lg');
iEle.innerHTML = baseUni + x;
return tgt.appendChild(iEle);
}
*,
*:before,
*:after {
box-sizing: border-box;
}
html,
body {
width: 100%;
height: 100%;
font: 400 16px/1.45'Verdana';
}
.search-bar {
display: table;
position: relative;
height: 80px;
padding: 5px 15px;
width: 500px;
}
.search-btn {
color: #fff;
display: table-cell;
line-height: 50px;
width: 35%;
background-color: black;
text-align: center;
padding: 10px 20px;
}
#inputText {
display: table-cell;
width: 25%;
padding: 0 5px;
font: 400 20px/30px'Consolas';
text-align: center;
vertical-align: middle;
}
#result {
color: #000;
line-height: 33px;
width: 35%;
background-color: #fff;
text-align: center;
vertical-align: middle;
border: 1px solid black;
}
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet" />
<fieldset class="search-bar">
<legend>Font-Awesome Unicode</legend>
<input id="inputText">
<i class="search-btn fa fa-lg"></i>
<i id="result" class="fa fa-lg"> </i>
</fieldset>
<p>Enter: 3 digit hexidecimal number.</p>
<p>Range: 000 - 2b1</p>
<p>Font-Awesome v.4.6.3</p>
SNIPPET 2
$(function() {
$(".mobile-search-icon").on('click', function(e) {
if ($(this).hasClass('on')) {
$(this).html('').toggleClass('on');
} else {
$(this).html('').toggleClass('on');
}
});
});
.search-bar {
color: #fff;
display: table-cell;
vertical-align: middle;
line-height: 30px;
width: 150px;
background-color: black;
text-align: center;
padding: 10px;
cursor: pointer;
}
.mobile-search-icon {
width: 100%;
}
.on {
opacity: 1;
transition: opacity 1s;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css">
<div class="search-bar">
<i class="mobile-search-icon fa fa-lg on"></i>
</div>