[Plain javascript]My mouseover effect is buggy - javascript

I'm trying to achieve a mouse hover effect using Js on three <li>s.
The effect doesn't work on the first try, I have to keep hovering my mouse again and again to go back to its original string.
note: i linked the script at the right before </body>
HTML code:
<ul>
<li data-value="// ABOUT" ><a href="#" >// ABOUT </a></li>
<li data-value="// PROJECTS" ><a href='#' >// PROJECTS </a></li>
<li data-value="// CONTACT" ><a href="#" >// CONTACT </a></li>
</ul>
Javascript code:
const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
let interval = null;
document.querySelectorAll("li").forEach(li => {
li.onmouseover = event => {
let iteration = 0;
clearInterval(interval);
interval = setInterval(() => {
event.target.innerText = event.target.innerText
.split("")
.map((letter, index) => {
if (index < iteration) {
return event.target.dataset.value[index];
}
return letters[Math.floor(Math.random() * 26)];
})
.join("");
if (iteration >= event.target.dataset.value.length) {
clearInterval(interval);
}
iteration += 1 / 3;
}, 30);
};
});

1- you don't need to use event.target because you only use arrow function.
2- the interval variable should be local to avoid conflicts in your multiples setInterval
3- your code remove all links -> ex <li><a href="#" >// ABOUT </a><li> by <li>// ABOUT <li>....
so, here it is... ( with some improvemnts )
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
document.querySelectorAll('li').forEach( li_Elm =>
{
let interval = null
, li_aLink = li_Elm.querySelector('a')
, textInit = li_aLink.textContent
, length = textInit.length
;
li_Elm.onmouseover =_=>
{
let iteration = 0;
clearInterval(interval);
interval = setInterval(() =>
{
li_aLink.textContent = Array
.from( ({length}), (letter,index) =>
( index < iteration)
? textInit[index]
: letters[Math.floor(Math.random() * 26)]
).join('');
if (iteration >= length)
clearInterval(interval);
iteration += 1 / 3;
}
, 30);
};
});
<ul>
<li><a href="#" >// ABOUT </a></li>
<li><a href='#' >// PROJECTS </a></li>
<li><a href="#" >// CONTACT </a></li>
</ul>

Related

Pagination with node js

I'm trying to solve that problem, but it seems that I'm missing something. So, I've watched that video about backend pagination https://www.youtube.com/watch?v=ZX3qt0UWifc, I've implemented it on my project, but now I have problems implementing that to frontend. My posts are showing correctly, according to that URL(ex: "/posts?page=3&limit=5") but I have a problem with the pagination bar, that one with <Prev 1 2 3 Next>. How can I make that page numbers generate based on how many posts I have and on what page I am actually. For example if i'm on page 3, it would be <Prev 2 [3] 4 Next>. 3 is active. And If I've reached the last page, it would be <Prev 5 6 [7].
I've hard-coded three 's href, but I'm pretty sure that is not the way to do it.
If someone could help me with that problem, I would be extremely grateful. Thank you in advance!
//posts.js
function paginatedResults(model) {
return async (req, res, next) => {
const page = parseInt(req.query.page);
const limit = parseInt(req.query.limit);
const startIndex = (page - 1) * limit;
const endIndex = page * limit;
const results = {};
if (endIndex < await model.countDocuments().exec()) {
results.next = {
page: page + 1,
limit: limit
};
}
if (startIndex > 0) {
results.previous = {
page: page - 1,
limit: limit
};
}
try {
results.results = await model.find().limit(limit).skip(startIndex).exec();
res.paginatedResults = results;
next();
} catch(e) {
res.status(500).json({ message: e.message});
}
};
}
router.get("/", paginatedResults(Post), function (req, res) {
var posts = res.paginatedResults;
res.render("posts/index", {posts: res.paginatedResults, limit: 2})
});
//index.ejs
<div class="col-sm-6">
<ul class="pagination">
<li>
<a href="#" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<li class="active">1</li>
<li>2</li>
<li>3</li>
<li>
<a href="#" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</div>
You might consider sending the information of how many pages are there in total. For example like this:
const allResults = await model.find().exec()
res.totalPages = Math.ceil(allResults.length / limit)
...
res.render("posts/index", {totalPages: res.totalPages, posts: res.paginatedResults, limit: 2})
Then on the ejs file you can start with an empty <ul class="pagination"> tag that you will inject with this script.
<script>
function generateUrl(page, limit) {
return `http://localhost:3000/posts?page=${page}&limit=${limit}`
}
function injectPagination() {
// get current page & total pages
const urlParams = new URLSearchParams(window.location.search)
const page = +urlParams.get("page")
const totalPages = +('<%- JSON.stringify(totalPages) %>')
// get the pagination ul
const ul = document.getElementsByClassName('pagination')[0]
// handle link to page - 2
let previous = page <= 2 ? false : true
let previousList = ''
if (previous) {
previousList = `
<li>
<a href="${generateUrl(page-2, 2)}" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
`
}
// handle link to page + 2
let next = page + 1 >= totalPages ? false : true
let nextList = ''
if (next) {
nextList = `
<li>
<a href="${generateUrl(page+2, 2)}" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
`
}
// handle link to page - 1
let beforePageActive = page - 1 < 1 ? false : true
let beforePageActiveList = ''
if(beforePageActive) {
beforePageActiveList += `<li>${page-1}</li>`
}
// handle link to page + 1
let afterPageActive = page + 1 > totalPages ? false : true
let afterPageActiveList = ''
if(afterPageActive) {
afterPageActiveList += `<li>${page+1}</li>`
}
// handle active page
const pageActiveList = `<li class="active">[${page}]</li>`
ul.innerHTML = previousList + beforePageActiveList + pageActiveList + afterPageActiveList + nextList
}
injectPagination()
</script>

append <li> if list item count < 3

I want to wrap every three < li > elements inside div and I did it. Now I want to count if there are less than 3 < li> inside that wrapped div, and if it is less than 3, then append < li >
This is my code so far, I don't know why it's not working:
var divs = $(".footer-events .event");
for(var i = 0; i < divs.length; i+=3) {
divs.slice(i, i+3).wrapAll("<div class='item-wrapper'></div>");
}
if ( $('.footer-events .item-wrapper li').length < 3 ) {
divs.append('<li class="event"></li>');
}
All I want is to add < li > to fill the remaining space if number of children is less then three.
Looks like you want to append <li> to some <div>, which is invalid.
Anyway: here's a minimal reproducable example (just plain old js) of what you apparently need.
[edit] A recursive and functional method for this
const uls = document.querySelectorAll("ul");
const createLi = () => Object.assign(
document.createElement("li"),
{className: "created"} );
uls.forEach( ul => {
const thisUl = ul.querySelectorAll("li");
if (thisUl.length < 3) {
let i = 3 - thisUl.length;
while(i--) {
ul.append(createLi());
}
}
});
.created::before {
content: 'hi, I am created';
}
<ul>
</ul>
<ul>
<li>item1</li>
</ul>
<ul>
<li>item1</li>
<li>item2</li>
</ul>
<ul>
<li>item1</li>
<li>item2</li>
<li>item3</li>
</ul>
Or a bit shorter/recursive
const stuffUntil = 4;
const appendLi = (root, reFillIfNecessary) => {
root.append(Object.assign(document.createElement("li"), {className: "created"}));
reFillIfNecessary();
};
const stuffIt = (len, ul) => len >= stuffUntil ? true :
appendLi(ul, () => stuffIt(len + 1, ul));
const stuffUl = ul => stuffIt(ul.querySelectorAll("li").length, ul);
document.querySelectorAll("ul").forEach(stuffUl);
.created::before {
content: "hi, I am just stuffing";
}
<ul></ul>
<ul>
<li>item1</li>
</ul>
<ul>
<li>item1</li>
<li>item2</li>
<li>item3</li>
</ul>

Replacing <li> image using javascript function

I am trying to replace the 3rd star in this list with an empty star based on a function. If the user has over 10 moves, the 3rd star should be replaced. After 14 moves, the 2nd star should also be replaced. I've tried the replaceChild Node but I can't get it working at all. Below is the UL list and as well as the function that I'm trying to associate it with. Is the replaceChild node the best method?
<div id="starRating">
<ul id="stars" class="starlist" style="list-style-type:none">
<li><img src="/images/filledstar.png" id="star1"> </li>
<li><img src="/images/filledstar.png" id="star2"> </li>
<li><img src="/images/filledstar.png" id="star3"> </li>
<li><img src="/images/emptystar.png" id="star4"> </li>
</ul>
</div>
function playerRating(moves) { //determines rating based on how many moves the player takes
var rating = 3;
if (moves > 10 && moves <= 13) {
rating = 2;
} else if (moves >= 14) {
rating = 1;
} else {
rating = 3;
} return rating;
document.getElementById("rating").innerHTML = "Rating: "+ rating;
}
You didn't explain what triggers the function, but it would work something like this:
// Get star references for empty and filled in
var filledStar1 = document.getElementById("filled1");
var filledStar2 = document.getElementById("filled2");
var filledStar3 = document.getElementById("filled3");
var emptyStar = document.getElementById("empty");
function playerRating(moves) {
var rating = 3;
if (moves > 10 && moves <= 13) {
filledStar3.src = emptyStar.src;
rating = 2;
} else if (moves >= 14) {
filledStar2.src = emptyStar.src;
filledStar3.src = emptyStar.src;
rating = 1;
}
return rating;
document.getElementById("rating").textContent = "Rating: " + rating;
}
// Try these one at a time and you will see the ratings work
playerRating(9);
playerRating(11);
playerRating(14);
img {width:50px;}
<div id="starRating">
<ul id="stars" class="starlist" style="list-style-type:none">
<li><img src="http://www.bombmanual.com/manual/1/html/img/symbols/2-filledstar.png" id="filled1"> </li>
<li><img src="http://www.bombmanual.com/manual/1/html/img/symbols/2-filledstar.png" id="filled2"> </li>
<li><img src="http://www.bombmanual.com/manual/1/html/img/symbols/2-filledstar.png" id="filled3"> </li>
<li><img src="https://cdn2.iconfinder.com/data/icons/snipicons/500/star-empty-m-128.png" id="empty"> </li>
</ul>
</div>
<p id="rating"></p>

Javascript / Add class to element with interval

Javascript isn't my forte, so I'm looking for help : How would you write a function which add a Class to 3 elements with interval ?
<ul>
<li class="panel">Item 1</li>
<li class="panel">Item 2</li>
<li class="panel">Item 3</li>
</ul>
The idea is to add an .--active class on the 1st item when document is ready and remove it after 2sec to add it to the 2nd item and so on.
If you're using jQuery you could loop through the li's using the index, and reset the index to 0 when you reach the last li element :
if( $('li.panel.active').index() == lis_count-1 )
active_li_index = 0;
else
active_li_index++;
Hope this helps.
jQuery solution:
$(function(){
var lis_count = $('li.panel').length;
var active_li_index = 0;
setInterval(function(){
if( $('li.panel.active').index() == lis_count-1 )
active_li_index = 0;
else
active_li_index++;
$('li.panel.active').removeClass('active');
$('li.panel').eq(active_li_index).addClass('active');
}, 1000);
})
.active{
background-color: yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li class="panel active">Item 1</li>
<li class="panel">Item 2</li>
<li class="panel">Item 3</li>
</ul>
Pure JS solution:
document.addEventListener('DOMContentLoaded', function(){
var lis = Array.prototype.slice.call( document.querySelectorAll('li.panel'));
var lis_count = lis.length;
var active_li_index = 0;
setInterval(function(){
var active_li = document.querySelector('li.panel.active');
if( lis.indexOf(active_li) == lis_count-1 )
active_li_index = 0;
else
active_li_index++;
active_li.classList.remove('active');
document.querySelectorAll('li.panel')[active_li_index].classList.add('active');
}, 1000);
}, false);
.active{
background-color: yellow;
}
<ul>
<li class="panel active">Item 1</li>
<li class="panel">Item 2</li>
<li class="panel">Item 3</li>
</ul>
Without jQuery:
function showGarland () {
var itemClass = 'panel';
var activeClass = '--active';
var wait = 2000; // 2 seconds
function toggleActive (element, index, maxIndex) {
setTimeout(function(){
element.classList.add(activeClass);
setTimeout(function(){
element.classList.remove(activeClass);
if (index == maxIndex) {
runLoop();
}
}, wait);
}, wait * index);
}
function runLoop () {
var allItems = document.getElementsByClassName(itemClass);
for (var index = 0; index < allItems.length; index++) {
var element = allItems[index];
toggleActive(element, index, allItems.length - 1);
}
}
runLoop();
}
window.addEventListener('load', showGarland);
.--active {
color:red;
}
<ul>
<li class="panel">Item 1</li>
<li class="panel">Item 2</li>
<li class="panel">Item 3</li>
</ul>
since you use jQuery you can do :
jQuery(() => { // callback when DOM is ready
$('.panel1').addClass('active'); // add your class
setTimeout(() => { // function that execute the callback after 2000ms (2s)
$('.panel1).removeClass('active'); // remove your class active
}, 2000);
});
you should use different class for your differents div
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<!-- <div class="hide">
text
</div> -->
<ul id="Items">
<li class="panel">Item 1</li>
<li class="panel hide">Item 2</li>
<li class="panel hide">Item 3</li>
</ul>
<style>
.hide{
visibility: hidden;
}
</style>
<script>
$(document).ready(function() {
var listItems = $("#Items li");
// alert(listItems);
listItems.each(function(idx, li) {
var product = $(li);
setInterval(function(){
product.css( "visibility", "visible" );
$(li).next().css( "visibility", "hidden" );
$(li).prev().css( "visibility", "hidden" );
}, 2000);
});
});
</script>
The above one works fine for two elements, but for the third element its showing quickly without displayinh the second element.
You can use something like this, you need to call toggleClass with the index you want to start, The functions addClass and removeClass supports multiple elements and multiple classes.
// Add class to element
// support multiple classes
function addClass(elements, className){
// split classes
var classArray = className.split(' ');
var els = [];
// If element does not have length property
if(elements.length == undefined)
els[0] = elements
else
els = elements;
for(e=0; e<els.length; e++){
var element = els[e];
for(i=0; i<classArray.length; i++){
if(element.className.indexOf(classArray[i])==-1){
element.className += ' ' + classArray[i];
}
}
}
}
// Remove class to element
// support multiple classes
function removeClass(elements, className){
var classArray = className.split(' ');
var els = [];
// If elements does not have length property
if(elements.length == undefined)
els[0] = elements
else
els = elements;
for(e=0; e<els.length; e++){
var element = els[e];
for(i= 0; i<classArray.length; i++){
element.className = element.className.replace(classArray[i], '').trim();
}
}
}
function toggleClass(index){
// get active elements and remove active class
removeClass(document.getElementsByClassName('active'), 'active');
// add class to element at index
addClass(document.getElementsByClassName('panel')[index], 'active');
// test if index should increment or reset
if(index<document.getElementsByClassName('panel').length - 1){
index++;
}else{
index = 0;
}
// wait 2sec until execute toggleClass again
setTimeout(toggleClass, 2000, index);
}
toggleClass(0);
.active {
color: green;
}
<ul>
<li class="panel">Item 1</li>
<li class="panel active">Item 2</li>
<li class="panel">Item 3</li>
</ul>
Edit: By the way beware with method classList since you'll need to polyfill for browser compatibility
window.addEventListener("load",function change(i=0){
var els=document.getElementsByClassName("panel");
if(els[i-1]) els[i-1].classList.toggle("active");
els[i].classList.toggle("active");
if(i<els.length-1) setTimeout(change,2000,i+1);
});
You could use a recursive approach to iterate over the class elements slowly and toggle their active class...
Without jQuery:
function showGarland () {
var itemClass = 'panel';
var activeClass = '--active';
var wait = 2000; // 2 seconds
function toggleActive (element, index, maxIndex) {
setTimeout(function(){
element.classList.add(activeClass);
setTimeout(function(){
element.classList.remove(activeClass);
if (index == maxIndex) {
runLoop();
}
}, wait);
}, wait * index);
}
function runLoop () {
var allItems = document.getElementsByClassName(itemClass);
for (var index = 0; index < allItems.length; index++) {
var element = allItems[index];
toggleActive(element, index, allItems.length - 1);
}
}
runLoop();
}
window.addEventListener('load', showGarland);
.--active {
color:red;
}
<ul>
<li class="panel">Item 1</li>
<li class="panel">Item 2</li>
<li class="panel">Item 3</li>
</ul>

Array of numbers, check to see which 2 numbers the current value is between

I have an array of numbers. 0,136,1084,3521,3961,5631,6510,7901,8204 (which are the current scrollTops of all of the sections on one page.) I'm trying to find a way to take the current window scrollTop and find out which of these values it's currently between so that when the page is being scrolled, the active navigation item switches.
Currently, while scrolling, 'current page' skips 0 and goes straight to 1 in the array, and as a result, is unable to catch the last page.
currentPage = 0;
divOffset = new Array();
function setCurrentPage() {
divOffset = []; //this ends up as 0,136,1084,3521,3961,5631,6510,7901,8204
//get offset and ID of each section and add to array.
$(".section").each(function() {
sectionOffset = $(this).offset();
divOffset.push(Math.round(sectionOffset.top));
});
bodyOffset = $(window).scrollTop();
for(i=0; i < divOffset.length; i++) {
if( divOffset[i] >= bodyOffset ) {
currentPage = i;
$('#mainNav li').removeClass("active");
$("#mainNav li #a-" + currentPage).parent().addClass("active");
return false;
}
}
}
My navigation looks something like this:
<ul id="mainNav">
<li class="active">home</li>
<li class="menuLI"><a>works</a>
<ul>
<li><a href='#a-1' class='navA' id='a-1'>Websites</a></li>
<li><a href='#a-2' class='navA' id='a-2'>Illustrations</a></li>
<li><a href='#a-3' class='navA' id='a-3'>Photomanipulations</a></li>
<li><a href='#a-4' class='navA' id='a-4'>Glam Guitars</a></li>
<li><a href='#a-5' class='navA' id='a-5'>Logos</a></li>
<li><a href='#a-6' class='navA' id='a-6'>Photography</a></li>
</ul>
</li>
<li>about</li>
<li>contact</li>
</ul>
You can look at it here: http://glowtagdesign.com/index2.php#a-0
Assuming the array is sorted, try this:
var i;
for (i = 0; i < arr.length && pos < arr[i]; i++)
{
}
// i is now the number of items in the array less than pos
// pos is less than the first item -> 0
// pos is greater than the last item -> arr.length

Categories