Remove all localstorage items with the value true? - javascript

I'd like to reset localstorage daily and remove a lot of the old storage but also keep a few important items for some users (localStorage.clear() not an option).
The keys I'd like removed all have the value true. The important items to keep have different values. Rather than listing every single item (200+) that I want to remove do you think it's possible to remove all at once by targeting the value of true?
As you can see from this JSfiddle example the one button adds three items to local storage and two of them have the true value.
$(".no").on('click', function() {
localStorage.removeItem(true)
});
$(".one").on('click', function() {
localStorage.setItem("one", true)
localStorage.setItem("two", "yes")
localStorage.setItem("three", true)
});
.no {
width: 40px;
padding: 10px 40px;
border: 2px solid black;
}
.one {
width: 40px;
padding: 10px 40px;
border: 2px solid black;
}
<div class="one">one</div>
<br>
<div class="no">clear</div>

Get list of item keys from localStorage:
const localStorageKeys = Object.keys(localStorage);
Check where value is true and remove:
for (let key of localStorageKeys) {
if (localStorage[key] === "true") {
localStorage.removeItem(key);
}
}

You can try with the below code.
const localStorageKeys = Object.keys(localStorage);
for (let key of localStorageKeys) {
/*Also check with True as well if value is added like this */
if (localStorage[key] === "true" || localStorage[key] === "True") {
console.log(key);
}
}

Related

JS Variable Cannot Change Element Properties

Quick question here, I encountered this problem today while practicing some JS. I wanted to create a basic prototype to loop through a "div" background-color array on click, but I realized that assigning the element property to a variable (instead of using the event target) impedes me to change the actual values.
This is the JS code:
let colors = ["blue", "yellow", "orange", "red"]
let n = 1;
document.querySelectorAll('div').forEach(occurence => {
occurence.addEventListener('click', (e) => {
let classes = e.target.className;
classes = colors[n];
n++;
console.log(classes);
if (n >= 4) {n = 0;}
});
});
So, changing the actual e.target.className works just fine, but trying to change the assigned "classes" variable does nothing. I feel like this may be a matter of specificity, or JS being unable to access the actual property values, or some akin beginner mistake.
e.target.className passes by value when you have let classes = e.target.className, so classes contains a copy of its data. Changing classes just changes the copy, rather than what's stored in e.target.classname.
Actually, you are not changing the value of e.target.className. What you do, is assigning the value of e.target.className to the variable/let-binding classes. To assign one of the color values to the className property, the assignment has to be the other way around:
e.target.className = colors[n];
let classes = e.target.className will assign the current string value of className to classes. And while you can assign a new colour value to classes that won't assign the new colour value to the className property of the element. For that you want to explicitly assign it: e.target.className = colors[i].
You may also want to remove the need to add a event listener to all the elements. Event delegation allows you to add one listener to a parent element which captures events from its child elements as they "bubble up" the DOM.
Here's an over-wrought example:
const colors = ['blue', 'yellow', 'orange', 'red'];
// Cache the elements first, and add a listener to
// the container
const counter = document.querySelector('.index');
const container = document.querySelector('.container');
container.addEventListener('click', handleClick);
let count = 0;
function handleClick(e) {
// Check to see if the element that was clicked
// was a div element
if (e.target.matches('.container div')) {
// Update the counter element, the `className` value,
// and the `textContent of the "box", and then update
// the count value
counter.textContent = `Color index: ${count}`;
e.target.className = colors[count];
e.target.textContent = colors[count];
count = count < colors.length - 1 ? ++count : 0;
}
}
.container { display: grid; gap: 0.4em; grid-template-columns: repeat(2, 50px); }
.container div { display: flex; justify-content: center; align-items: center; height: 50px; width: 50px; border: 1px solid lightgray; }
.container div:hover { cursor: pointer; border: 1px solid darkgray; }
.blue { background-color: lightblue; }
.yellow { background-color: #ffff00; }
.orange { background-color: darkorange; }
.red { background-color: red; }
.index { margin-top: 0.5em; }
<div class="container">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<div class="index">Color index:</div>

How do I filter an array by display type if display type is set by bootstrap?

I am trying to filter an array of portfolio cards so that I can see which items are displayed or not.
let pfCard = document.getElementsByClassName("PortfolioCard")
const visibleCards = [...document.getElementsByClassName("PortfolioCard")].filter(x => x.style.display != "none");
console.log(visibleCards.length);
The displayed cards are having the display set to none by a bootstrap class when the media query is met. The code I have been trying to run still grabs all the elements in the array no matter the display type. There are 8 elements in the array and 2 are set to display: none; currently.
Because the element's display property is set by the CSS file, you'll need to get the computed style using window.getComputedStyle:
let pfCard = document.getElementsByClassName("PortfolioCard")
const visibleCards = [...document.getElementsByClassName("PortfolioCard")].filter((x) => {
return window.getComputedStyle(x).display != "none"
});
console.log(visibleCards.length);
.PortfolioCard {
width: 100px;
height: 100px;
background: red;
margin: 10px;
}
.PortfolioCard:nth-child(3) {
display: none;
}
<div class="PortfolioCard"></div>
<div class="PortfolioCard"></div>
<div class="PortfolioCard"></div>

Find specific div in forEach

I'm wondering how to select a child when the callback of a forEach does not directly refer to the desired element.
const $searchField = document.querySelectorAll('.js-search');
if ($searchField.length > 0) {
$searchField.forEach(function($el) {
// $el = .js-search
$el.addEventListener('click', function(event) {
$el.classList.add('is-active');
});
// Here, i need to target the input, not .js-search
$el.addEventListener('keyup', function(event) {
if ($el.value.length > 0) {
$el.classList.add('is-active');
} else {
$el.classList.remove('is-active')
}
//});
});
}
Here, $el refers to the parent .js-search so obviously the keyup can not work. I would like to make sure to select the input, but I'm not sure how to do it properly.
Demo is available on Codepen!
The goal is to keep the state is-active when the search is completed (has at least 1 character).
Thanks
Just select the child input's .value, instead of the $el's .value:
const $searchField = document.querySelectorAll('.js-search');
$searchField.forEach(function($el) {
// $el = .js-search
$el.addEventListener('click', function() {
$el.classList.add('is-active');
});
const input = $el.querySelector('input');
input.addEventListener('keyup', function() {
if (input.value.length > 0) {
$el.classList.add('is-active');
} else {
$el.classList.remove('is-active')
}
});
});
Note that there's no need for the if check if you don't want - calling forEach on an empty collection won't throw an error, it just won't iterate over anything.
You also might consider including a polyfill for NodeList.prototype.forEach (if you aren't already), since older browsers don't support it. (Alternatively, transform the collection into an array, or use Array.prototype.forEach.call)
A bit opinion-based, but there's no need to prefix variable names with $ - this isn't PHP. Often, a $ prefix is done when indicating that something is a jQuery ($) collection. If you're doing DOM manipulation, probably best not to use a variable name that starts with $, to avoid confusion for future readers of your code.
You can use this.querySelector('input'); where this will the parent element and querySelector will get the first input child element
const $searchField = document.querySelectorAll('.js-search');
if ($searchField.length > 0) {
$searchField.forEach(function($el) {
$el.addEventListener('click', function(event) {
$el.classList.add('is-active');
let $input = this.querySelector('input');
$input.addEventListener('keyup', function(et) {
console.log(et.target.value)
});
});
});
document.addEventListener('click', function(event) {
if (!$(event.target).closest('.js-search').length) {
closeSearchs();
}
$('.js-search.is-active').not($(event.target).closest('.js-search')).removeClass('is-active');
});
}
function closeSearchs() {
$searchField.forEach(function($el) {
$el.classList.toggle('is-active');
});
}
.field-search {
position: relative;
&.is-active {
.search-input {
background-color: rgba(0, 0, 0, .35);
color: white;
}
}
.search-input {
background: none;
box-shadow: none;
border-radius: 4px;
color: black;
font-size: 1.2rem;
font-weight: 400;
height: 100%;
outline: none;
padding-left: 4.5rem;
transition: background-color .2s;
width: 365px;
&::placeholder {
color: black;
font-size: 1.2rem;
font-weight: 400;
}
&.is-active {
background-color: rgba(255, 255, 255, .1);
}
}
}
<div class="field-search js-search">
<input type="search" class="search-input" placeholder="Search...">
</div>

Optimizing jquery hide() and show() on Safari

I have a site with a large outline, and I'm trying to let our users filter it down so they can see just the stuff they want. Each line of the outline has a set of classes that say what category it's in, and I'm hide/showing them via jQuery when the users select a particular category.
Here's the current location so you can see it in action:
https://courses.edx.org/courses/course-v1:HarvardX+CHEM160+1T2017/76695c0ad7604bb897570ecb906db6e3/
And here's the javascript and css for this page:
$(document).ready(function() {
console.log('working');
// Keeping track of all the currently visible items.
var currentlyShown = [];
var index;
var showAllButton = $('#showAll');
// If any of the object's classes match any of the selected options, show it.
function showRightClasses() {
console.log('showing: ' + currentlyShown);
if (currentlyShown.length == 0) {
showAllButton.click();
}
$('.hiddenpage').each(function(i) {
if (_.intersection(this.className.split(' '), currentlyShown).length > 0) {
$(this).show('slow');
} else {
$(this).hide('slow');
}
});
}
if (showAllButton.prop('checked')) {
currentlyShown.push('hiddenpage');
showRightClasses();
}
showAllButton.change(function() {
if (!this.checked) {
index = currentlyShown.indexOf('hiddenpage');
if (index !== -1) {
currentlyShown.splice(index, 1);
}
} else {
currentlyShown.push('hiddenpage');
}
showRightClasses();
});
$('.pageselector').change(function() {
subject = $(this).attr('name');
if (!this.checked) {
index = currentlyShown.indexOf(subject);
if (index !== -1) {
currentlyShown.splice(index, 1);
}
} else {
currentlyShown.push(subject);
}
if (showAllButton.prop('checked')) {
showAllButton.click();
}
showRightClasses();
});
});
.hiddenpage {
display: none;
}
.checkboxes {
float: right;
padding: 8px;
border: 4px outset #aaa;
border-radius: 8px;
background-color: #eee;
}
.checkboxes label {
display: inline;
}
.nav-section {
font-size: 120%;
font-weight: bold;
margin-top: 1em;
}
.nav-sub {
font-weight: bold;
margin-left: 1em;
}
.nav-unit {
font-weight: normal;
margin-left: 2em;
}
This works, but on Safari it's dreadfully slow, and it's not particularly fast on Firefox either. Is there a more efficient way to hide/show the rows in this outline without losing the animation? Am I accidentally doing something foolish like having every row run code that hides every other row?
I should note that I have no ability to control the rest of the environment. I can't change the version of jQuery that the site uses, or remove Underscore, for example. I can only control the code you see above, and the HTML for the list.
First of all, if you care about speed, ditch the 'slow' param in .show('slow') and .hide('slow'). This triggers a very performance-heavy jQuery animation.
With all the frames you're loosing right now, this will not work nice anyway. If you need animation there, maybe you could try something with opacity instead, since (css-based) opacity animation is very cheap.
EDIT: just checked this on the site you linked and it works nice and snappy with just .show() and .hide(). The 'slow' param is definitely your bottleneck, so either just remove it or look for a different way to animate, if you absolutely need to.

Is it possible to loop through the style attributes of a div with javascript or jquery?

As the title says I am wondering if it is possible to loop through the style attributes of a div with javascript or jquery. What I want to do is loop through the styles and create an object containing these style names and values.
Here is an example of what I am trying to do:
alert($('#mydiv').attr('style'));
Gives the following:
background-color: #CCCCCC; border-width: 2px; border-style: solid; width: 250px;
And I want to create a object which looks like this:
{"background-color":"#CCCCCC","border-width":"2px","border-style":"solid","width":"250px"}
What I can't figure out is whether this is achievable by looping through the styles or whether I will have to create the object myself using code similar to below:
var style = {};
style['width'] = $('#mydiv').css('width');
Any input of this would be appreciated.
Version 1, using inline style
const style = $("#myDiv").attr("style");
const parts = style.split(";")
console.log(parts)
let obj = {}
parts.forEach(part => {
if (part.length > 0) { // skip the empty element after the last ;
const [key,val] = part.split(':');
obj[key] = val.trim();
}
})
console.log(obj)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<div id="myDiv" style="background-color: #CCCCCC; border-width: 2px; border-style: solid; width: 250px;">My div</div>
Version 2 using computed style of a list of elements
// more code needed to handle rgba
const rgbToHex = rgb => '#' + (rgb.match(/[0-9|.]+/g).map((x, i) => i === 3 ? parseInt(255 * parseFloat(x)).toString(16) : parseInt(x).toString(16)).join('')).padStart(2, '0').toUpperCase();
let obj = {}
const style = window.getComputedStyle(document.getElementById('myDiv')); //
["background-color", "border-width", "border-style", "width"]
.forEach(rule => {
const val = style.getPropertyValue(rule)
obj[rule] = val.includes('rgb') ? rgbToHex(val) : val;
})
console.log(obj)
#myDiv {
background-color: #CCCCCC;
border-width: 2px;
border-style: solid;
width: 250px;
}
<div id="myDiv">My div</div>
For <div id="id" style="color: red"/>, $('#id').attr('style') will return a string color: red so i guess, you can't loop them directly.
However you can create an array string.split(';') and loop over them.
But CSS associated with in inside <style> tag or some css file, i don't think you can get it. However, I am not sure.

Categories