Drop down menu that runs functions not working - javascript

I am trying to make a store locator and I am having problems with the drop down menu, I wanted a drop down menu for the distance from the user to the shops so for example 1 mile 3 miles 10 miles and so on but I can't get a drop down menu to run a function that will show these locations I have tried a couple example I have seen on here for example
Calling javascript functions from drop down
But I can't get it to work for my code the code I am trying to run is:
function list() {
document.getElementById("myList").onchange = function() {
var sheet=document.getElementById("myList").value;
if(sheet == "one"){
showPosition();
}
else if(sheet == "two"){
showPosition2();
}
else if(sheet = "three"){
}
else{
}
return false
};
}
window.onload = list;
The function that I want it to run when I choose the first option in the menu is:
function showPosition(position) {
var locations = [
['store1', -2.063150, 52.516503, 4],
['store2', -2.064824, 52.518436, 5],
['store3', -2.068214, 52.519898, 3],
['store4', -2.068558, 52.512769, 2],
['store5', -2.070875, 52.510758, 1]
];
var lon1 = position.coords.longitude* 0.0174532925;
var lat1 = position.coords.latitude * 0.0174532925;
var i = 0;
while (i < locations.length)
{
x.innerHTML+= "<br>Distance " + calcDist(lon1, lat1, locations[i][1]*0.0174532925, locations[i][2]* 0.0174532925);
i++;
}
}
function calcDist(lon1, lat1, lon2, lat2)
{
return Math.acos(Math.sin(lat1)*Math.sin(lat2) +
Math.cos(lat1)*Math.cos(lat2) *
Math.cos(lon2-lon1)) * 3958;
}
And the menu that I'm using is:
<ul id="dropdown">
<li> Choose theme
<ul>
<li id="stylesheet1" > Default </li>
<li id="stylesheet2" > Theme 1 </li>
<li id="stylesheet3" > Theme 2 </li>
<li id="stylesheet4" > Theme 3 </li>
</ul>
</li>
</ul>

By looking at the code, I see one major error.
Change window.onload = list; to window.onload = list();
Demo - shows the passing of params
var testPosition = {
coords: {
longitude: 1,
latitude: 1
}
}
showPosition(testPosition);

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>

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>

Pagination, bootstrap and knockout

So I don't have the option to use external third party libraries that would make my life so much easier, so I have to build this from scratch.
And I think I over complicated this.
So the idea is that if a client passes in, via the url, something like: showPages=4. Then we have something like:
<< < 1 2 3 4 _5_ 6 7 8 9 > >>
Where 5 is the current page, you should see 4 to the left and 4 to the right of the current page (lets say the max pages are 20)
My view model, so far, looks like:
var ViewModel = {
self.pagesBefore = ko.observable(4);
self.pagesAfter = ko.observable(4);
self.currentPage = ko.observable(5);
self.totalPages = ko.observable(20);
self.pageStart = ko.observable(0);
self.pageEnd = ko.observable(0);
}
I think I overcomplicated this because I don't think I need the pages before and pages after, I think I can change that into: self.showPages(4)
The problem I am having is laying this out in the view, I have no issue figuring out where to start, so for example there might be a method in the view model that does:
var endPage = self.currentPage() + self.pagesAfter();
if (endPage <= self.totalPages()) {
self.pageEnd(endPage); // In our example above its 9
} else {
self.pageEnd(self.totalPages());
}
To get the "final" page to the right, in this case 9.
And another that calculates what the "starting page (or pages before the current page)" is:
var startPage = self.currentPage() - self.pagesBefore();
if (startPage > 1) {
self.pageStart(startPage); // In our example above its 1
} else {
self.pageEnd(1);
}
So I would end up with a view model variables looking like:
var ViewModel = {
self.pagesBefore = ko.observable(4);
self.pagesAfter = ko.observable(4);
self.currentPage = ko.observable(5);
self.totalPages = ko.observable(20);
self.pageStart = ko.observable(1);
self.pageEnd = ko.observable(9);
}
So my question is: How do I translate this to the view so that I have a paginated element that has 5 selected with 4 to the right and 4 to the left with a total of 20 pages?
It is probably easier to get your JavaScript to make an array of objects to show on the page. You can do this with a ko.computed function.
Then to display it, you can just use a knockout foreach in your HTML.
As it is a computed function, changing the currentPage or totalPage observable will automatically cause your HTML to update.
Something like:
function ViewModel() {
var self = this;
self.currentPage = ko.observable(5);
self.totalPages = ko.observable(20);
self.pagesBefore = ko.observable(4);
self.pagesAfter = ko.observable(4);
self.pages = ko.computed(function() {
// Work out the start and end pages - same logic as you already had
var startPage = self.currentPage() - self.pagesBefore();
if (startPage < 1) {
startPage = 1
}
var endPage = self.currentPage() + self.pagesAfter();
if (endPage > self.totalPages()) {
endPage = self.totalPages();
}
// Make the page array
var pageArray = [];
for (var i = startPage; i <= endPage; i++) {
pageArray.push({ page: i, highlight: i == self.currentPage() });
}
return pageArray;
});
}
ko.applyBindings(new ViewModel());
<!-- Stuff to get the snippet to work -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<style>
ul li {
display: inline;
}
.high { color: red; }
</style>
<!-- output the pages (the important bit) -->
<ul data-bind="foreach: pages">
<li data-bind="text: page, css: (highlight ? 'high' : '')"></li>
</ul>

.change() Jquery not working

I have a series of elements with class xyz. The requirement is a user needs to input one number in them only. I then need to concat them. For example, their meter would read 1 2 0 0.
I have below.
$('.xyz').change(function () {
console.log($(this));
arrayNums.length = 0;
var thisRead = parseInt(CombinedNumber(arrayNums), 10);
var lastRead = parseInt($('.abcd').html(), 10);
if ((thisRead - lastRead) <= 1) {
$('#idthing').html("Your Last reading compared to this reading is " + (thisRead - lastRead) + " KWH <br>Are you SURE you want to submit this Reading?");
$('#idthing').attr('style' , 'color: red !important');
}
else { $('#idthing').text(thisRead - lastRead + ' KWH');}
});
function CombinedNumber(arrayNums) {
combined = "";
$('.xyz').each(function (index) {
arrayNums.push($(this).val());
});
$.each(arrayNums, function (index, value) {
combined += value;
});
console.log(combined);
return combined
This works the first time, if the user clicks off of the area, before hitting the submit button, but they might hit the submit button first.
Additionally, it ceases to do the calculation after the first time it happens!
I am trying to get a responsive calculation each time someone enters a number. Here is the DOM portion.
<div>
<ul>
<li>
<input class="xyz">
</li>
<li>
<input class="xyz">
</li>
</ul>
</div>
Use input event instead of change as change handler will be invoked only when focus of the element is lost.
var arrayNums = [];
$('.xyz').on('input', function() {
console.log($(this));
arrayNums.length = 0;
var thisRead = parseInt(CombinedNumber(arrayNums), 10);
var lastRead = parseInt($('.abcd').html(), 10);
if ((thisRead - lastRead) <= 1) {
$('#idthing').html("Your Last reading compared to this reading is " + (thisRead - lastRead) + " KWH <br>Are you SURE you want to submit this Reading?");
$('#idthing').attr('style', 'color: red !important');
} else {
$('#idthing').text(thisRead - lastRead + ' KWH');
}
});
function CombinedNumber(arrayNums) {
combined = "";
$('.xyz').each(function(index) {
arrayNums.push($(this).val());
});
$.each(arrayNums, function(index, value) {
combined += value;
});
console.log(combined);
return combined;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div>
<ul>
<li>
<input class="xyz">
</li>
<li>
<input class="xyz">
</li>
</ul>
</div>
If you are creating inputs having class .xyz dynamically (May be on click event of button) then below code would be work here. try to replace $('.xyz').change(function () with $(document).on('change','.xyz',function ()
$(document).on('change','.xyz',function () {
console.log($(this));
arrayNums.length = 0;
var thisRead = parseInt(CombinedNumber(arrayNums), 10);
var lastRead = parseInt($('.abcd').html(), 10);
if ((thisRead - lastRead) <= 1) {
$('#idthing').html("Your Last reading compared to this reading is " + (thisRead - lastRead) + " KWH <br>Are you SURE you want to submit this Reading?");
$('#idthing').attr('style' , 'color: red !important');
}
else { $('#idthing').text(thisRead - lastRead + ' KWH');}
});
Try to use jQuery Instead of $
jQuery('.xyz').change(function () {
console.log($(this));
arrayNums.length = 0;
var thisRead = parseInt(CombinedNumber(arrayNums), 10);
var lastRead = parseInt($('.abcd').html(), 10);
if ((thisRead - lastRead) <= 1) {
$('#idthing').html("Your Last reading compared to this reading is " + (thisRead - lastRead) + " KWH <br>Are you SURE you want to submit this Reading?");
$('#idthing').attr('style' , 'color: red !important');
}
else { $('#idthing').text(thisRead - lastRead + ' KWH'); }
});
function CombinedNumber(arrayNums) {
combined = "";
$('.xyz').each(function (index) {
arrayNums.push($(this).val());
});
$.each(arrayNums, function (index, value) {
combined += value;
});
console.log(combined);
return combined;
}

Google Maps Marker Filtering

I have the following jsfiddle:
http://jsfiddle.net/inkedraskal/zeLon7cs/2/
You'll notice when the map loads all the points are showing, and filtering on its own seems to work correctly. However, when you click a filter, (project for example), it will show only projects, but if you click it again, it wont reset the markers. As well as, if you say click News and then Stuff, then click Stuff again, it wont show the markers only applying to news again (still shows markers that apply to both News and Stuff, instead of just News).
Any tips would greatly be appreciated.
I have create a list-items to filter the markers on click
<div class="filters">
<ul>
<li>
Projects
</li>
<li>
News
</li>
<li>
stuff
</li>
</ul>
<ul>
<li>
Jan
</li>
<li>
Feb
</li>
<li>
Mar
</li>
</ul>
</div>
<div class="clear"></div>
at the bottom my JS you'll see the following:
$(document).on('click', '.filters a', function (event) {
event.preventDefault();
var $target = $(event.target);
var type = $target.data('filterby');
var value = $target.data('filtervalue');
$(this).toggleClass('active');
$.each(map.markers, function () {
if (this.filter[type] && this.filter[type].indexOf(value.toString()) >= 0) {
if (this.map == null) {
this.setMap(map);
}
} else {
this.setMap(null);
}
});
});
In your loop to hide/show each marker, for each individual marker you are only checking if that marker has the clicked on filter value. You need to check each marker against every active filter.
$(document).on('click', '.filters a', function(event) {
event.preventDefault();
$(this).toggleClass('active');
// create an array of active filters
var activeFilters = [];
$(".filters a.active").each(function() {
activeFilters.push({
by: $(this).data("filterby"),
value: $(this).data("filtervalue").toString()
});
});
if (activeFilters.length === 0) { // if there are no active filters, show all markers
for (var i = 0; i < map.markers.length; i++) {
map.markers[i].setMap(map);
}
} else {
for (var i = 0; i < map.markers.length; i++) { // for each marker
var marker = map.markers[i]; // just to make the next code easier
var missingActiveFilter = false;
// check if marker has every active filter
for (var j = 0; j < activeFilters.length; j++) {
var filter = activeFilters[j];
if (filter.by === "type" && marker.filter.type.indexOf(filter.value) === -1 || filter.by === "date" && marker.filter.date.indexOf(filter.value) === -1) {
missingActiveFilter = true;
break;
}
}
if (missingActiveFilter) {
marker.setMap(null);
} else {
marker.setMap(map);
}
}
}
});
});
Updated JSFiddle

Categories