I have successfully been able to filter, for instance shakemaps, by attribute id - I have successfully achieved this multiple ways - but all ending with the same problem.
1.) with the where filter 2.) definition expression 3.) iterating through all attribute ids and bringing them back.
The problem: All exists for only allowing/displaying one per attribute id at a time.. my goal is to feed the attribute ids into a checkbox list (which I have done), but allowing for items via attribute id to be added to the map as they are checked, collectively, one at a time - currently I can not seem to get this to work with the aspect of having multiple or more then one appear at a time on the map.
1.) i.e. the below filter (attempted logic 1) & CodePen:
.....
function filterByID(event) {
const selectedID = event.target.getAttribute("data-id");
eqLayerView.filter = {
where: "id = '" + selectedID + "'"
};
}
view.whenLayerView(fl)
.then(function(layerView) {
eqLayerView = layerView;
eqLayerView.filter = {
where: "id = ''"
};
.............
2.) i.e. another attempted logic (adding multiple at a time here, line by line, or via array):
layer.definitionExpression = "id = 'us70008jr5'",
layer.definitionExpression = "id = 'cgi988jr52'",
3.) i.e. 3rd attempt with a suggestion here on GIS exchange: https://gis.stackexchange.com/questions/364578/loop-through-attribute-ids-of-featurelayer
layer
.load()
.then(() => {
// create a query from the layer
const query = layer.createQuery();
query.returnDistinctValues = true;
query.where = "grid_value > 2"; // or 1=1 if you need them all
// the field you want distinct values for
query.outFields = ["id"];
return layer.queryFeatures(query);
})
.then(({ features }) => {
// extract the ids to a list
const ids = features.map(({ attributes }) => attributes.id);
return ids;
})
.then((ids) => {
// You can store them how you want, in this case,
// I put them in a dropdown select menu
const fragment = document.createDocumentFragment();
ids.forEach((id) => {
const option = document.createElement('option');
option.value = id;
option.innerText = id;
fragment.appendChild(option);
});
list.appendChild(fragment);
map.add(layer);
});
All attempted logic above result in the toggling of a shakemap by attribute id to be displayed only one at a time — by toggling a new on, the previous turns off, I need the ability to have multiple being able to exist on the map at once.
ArcGIS - javascript - 4.15
I am pretty sure I answer this question a couple of weeks ago, but I can't find my answer so maybe I just thought I did, in that case sorry about that.
Anyway, with a couple of fixes your code should work. The main issue here I think is that your query is not correct if you are going to use checkboxs. You need to use a set query like IN. I would be fine if you use radio buttons.
Here you have your example with the fixes I mention,
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<title>Filter features by attribute - 4.15</title>
<link rel='stylesheet' href='https://js.arcgis.com/4.15/esri/themes/light/main.css' />
<script src='https://js.arcgis.com/4.15/'></script>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 500px;
width: 100%;
}
</style>
<script>
require([
'esri/views/MapView',
'esri/Map',
'esri/layers/FeatureLayer',
'esri/widgets/Expand'
], function (MapView, Map, FeatureLayer, Expand) {
const layer = new FeatureLayer({
url: 'https://services9.arcgis.com/RHVPKKiFTONKtxq3/arcgis/rest/services/USGS_Seismic_Data_v1/FeatureServer/1/',
outFields: ['*']
, definitionExpression: 'eventTime >= CURRENT_TIMESTAMP - 30 AND grid_value > 2'
});
const map = new Map({
basemap: 'gray-vector'
, layers: [layer]
});
const view = new MapView({
map: map,
container: 'viewDiv',
center: [-98, 40],
zoom: 4
});
let eqLayerView;
let selectedIDs = {};
function updatedFilter() {
const ids = [];
for(const [key, value] of Object.entries(selectedIDs)) {
if (value) {
ids.push(`'${key}'`);
}
}
eqLayerView.filter = {
where: `id IN (${ids.join(',')})`
};
console.log(`[updateFilter] ${eqLayerView.filter.where}`);
}
const idElement = document.getElementById('id-filter');
idElement.addEventListener('click', filterByID);
function filterByID(event) {
const chk = event.target;
console.log(`[filterByID] ${chk.getAttribute('data-id')} ${chk.checked}`);
selectedIDs[chk.getAttribute('data-id')] = chk.checked;
updatedFilter();
}
view.whenLayerView(layer).then(function (layerView) {
eqLayerView = layerView;
updatedFilter();
var query = layer.createQuery();
query.outFields = ['id'];
query.returnDistinctValues = true;
query.returnGeometry = false;
layer.queryFeatures(query).then(function (results) {
results.features.map(function (feat) {
let id = feat.attributes['id'];
let opt = document.createElement('input');
opt.type = 'checkbox';
let label = document.createElement('label')
label.innerHTML = id;
opt.className = 'id-item visible-id';
opt.setAttribute('data-id', id);
idElement.appendChild(opt);
idElement.appendChild(label);
selectedIDs[id] = false;
});
});
});
});
</script>
</head>
<body>
<div id='id-filter' class='esri-widget'>
</div>
<div id='viewDiv'></div>
</body>
</html>
BTW, I added some logs for you to see what I consider import info of the events.
Last thing, I use an auxiliary dictionary to keep track of the visible features to filter. Another way to do it would simply to query all the checkbox and add the id to the filter if it is checked.
Related
Hi guys am having trouble with this, When I declare the elements outside the loop the script ends up being broken. I plan to use the elements in several functions. Tried a lot of times but didn't find a solution. Broke the project a few times while doing guess work. Also If there's any idea on how to shorten this or to make it work better please give me a heads up :)
async function loadTable() {
try {
while(dpTable.firstChild) dpTable.removeChild(dpTable.firstChild);
const result = await fetch('http://localhost:5000/users/student_data', { method: 'GET' });
const table = await result.json();
table.forEach((res) => {
//Create Table elements and then load results into the elements
const tr = document.createElement('tr');
const studentNo = document.createElement('td');
const name = document.createElement('td');
const surname = document.createElement('td');
const date_joined = document.createElement('td');
const fees_paid = document.createElement('td');
const fees_owing = document.createElement('td');
const is_owing = document.createElement('td');
const trash_btn = document.createElement('button');
const trash_Icon = document.createElement('i');
tr.id = 'Content';
studentNo.id = res.id;
name.id = 'fname';
surname.id = 'lname';
fees_paid.id = 'amount_paid';
fees_owing.id = 'amount_owing';
is_owing.id = 'debt';
//Enter the data from the response
studentNo.innerText = res.id;
name.innerText = res.fname;
surname.innerText = res.lname;
fees_paid.innerText = res.amount_paid;
fees_owing.innerText = res.amount_owing;
date_joined.innerText = res.date_joined;
is_owing.innerText = res.is_owing;
trash_btn.innerText = 'Delete';
//Add Classes for elements
studentNo.classList.add('trContent');
name.classList.add('trContent');
surname.classList.add('trContent');
fees_paid.classList.add('trContent');
fees_owing.classList.add('trContent');
date_joined.classList.add('trContent');
is_owing.classList.add('trContent');
trash_Icon.classList.add('fas');
trash_Icon.classList.add('fa-trash');
trash_Icon.classList.add('trash-btn');
//Append table row elements to main table
tr.append(studentNo);
tr.append(name);
tr.append(surname);
tr.append(fees_paid);
tr.append(fees_owing);
tr.append(date_joined);
tr.append(is_owing);
tr.append(trash_btn);
//Event Listeners
trash_btn.addEventListener('click', async (e) => {
if (window.confirm('Delete')) {
console.log('trash icon clicked');
const jsonReq = {};
jsonReq.id = res.id;
const result = await fetch('http://localhost:5000/users/student_data', {
method: 'DELETE',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(jsonReq),
});
alert('Deleted Record');
window.location.reload();
}
});
//Append the table as a child to the main element
dpTable.appendChild(tr);
});
} catch (e) {
console.log(`Cant load List ${e}`);
}
}
There are a couple of minor issues you need to remedy or perhaps even ignore
Element IDs must be unique so you can't use the same id for multiple cells. You could switch to using classNames instead
The buttons need to be inserted into cells, they can't be directly insterted into a row
You can cut this all down quite a bit by using an array of field names to iterate and using TableElement.insertRow() and TableRow.insertCell()
Something like:
const fields = ['studentNo','fname','lname','amount_paid','amount_owing','date_joined','is_owing'] ;
table.forEach((res) => {
const row = dpTable.insertRow();
// loop over fields array to create cell for each
fields.forEach((f) => {
const cell = row.insertCell();
cell.innerText = res[f];
cell.className = 'trContent';
});
const btnCell = row.insertCell();// cell for button
const trash_btn = document.createElement('button');
const trash_Icon = document.createElement('i');
trash_Icon.classList.add('fas', 'fa-trash', 'trash-btn');
trash_btn.append(trash_icon);
btnCell.append(trash_btn);// insert button in own cell
trash_btn.addEventListener('click', .....);
});
It is likely because you're declaring them as const which means they shouldn't be rewritten - but you have them in a loop which reassigns them and so causes an error. You can use let instead in this case
async function loadTable() {
try {
while(dpTable.firstChild) dpTable.removeChild(dpTable.firstChild);
const result = await fetch('http://localhost:5000/users/student_data', { method: 'GET' });
const table = await result.json();
let tr, studentNo, name, surname, date_joined, fees_paid, fees_owing, is_owing, trash_btn, trash_Icon
table.forEach((res) => {
//Create Table elements and then load results into the elements
tr = document.createElement('tr');
studentNo = document.createElement('td');
name = document.createElement('td');
surname = document.createElement('td');
date_joined = document.createElement('td');
fees_paid = document.createElement('td');
fees_owing = document.createElement('td');
is_owing = document.createElement('td');
trash_btn = document.createElement('button');
trash_Icon = document.createElement('i');
tr.id = 'Content';
studentNo.id = res.id;
name.id = 'fname';
surname.id = 'lname';
fees_paid.id = 'amount_paid';
fees_owing.id = 'amount_owing';
is_owing.id = 'debt';
//Enter the data from the response
studentNo.innerText = res.id;
name.innerText = res.fname;
surname.innerText = res.lname;
fees_paid.innerText = res.amount_paid;
fees_owing.innerText = res.amount_owing;
date_joined.innerText = res.date_joined;
is_owing.innerText = res.is_owing;
trash_btn.innerText = 'Delete';
//Add Classes for elements
studentNo.classList.add('trContent');
name.classList.add('trContent');
surname.classList.add('trContent');
fees_paid.classList.add('trContent');
fees_owing.classList.add('trContent');
date_joined.classList.add('trContent');
is_owing.classList.add('trContent');
trash_Icon.classList.add('fas');
trash_Icon.classList.add('fa-trash');
trash_Icon.classList.add('trash-btn');
//Append table row elements to main table
tr.append(studentNo);
tr.append(name);
tr.append(surname);
tr.append(fees_paid);
tr.append(fees_owing);
tr.append(date_joined);
tr.append(is_owing);
tr.append(trash_btn);
//Event Listeners
trash_btn.addEventListener('click', async (e) => {
if (window.confirm('Delete')) {
console.log('trash icon clicked');
const jsonReq = {};
jsonReq.id = res.id;
const result = await fetch('http://localhost:5000/users/student_data', {
method: 'DELETE',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(jsonReq),
});
alert('Deleted Record');
window.location.reload();
}
});
//Append the table as a child to the main element
dpTable.appendChild(tr);
});
} catch (e) {
console.log(`Cant load List ${e}`);
}
}
You can definitely simplify your code and make it more flexible into the process. The JSON data returned will have a set of keys - these can be used to build the HTML output without knowing, in advance, what they are. The beauty of that is the same code can display greatly different datasets very easily.
If you were to use the method create shown below to create the new DOM elements you greatly reduce the code needed.
<?php
#---------------------------------------------------
# to emulate whatever "users/student_data" returns.
# this is dummy data for example only.
#
set_include_path('c:/wwwroot/dbo/');
require 'db-conn-details.php';
require 'mysqli-conn.php';
/*
When we receive the AJAX request, identified by querystring,
prepare some sql statement and execute. Return the full
recordset in JSON format.
*/
if( isset( $_GET['fetch'] ) && $_GET['fetch']=='data' ){
ob_clean();
$sql='select * from `sport` limit 10';
$res=$db->query( $sql );
$json=json_encode( $res->fetch_all( MYSQLI_ASSOC ) );
exit( $json );
}
if( $_SERVER['REQUEST_METHOD']=='DELETE' ){
exit('DELETE');
}
?>
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8' />
<title></title>
<style>
#data{ border:1px solid grey;display:table;padding:1rem;width:100%; }
</style>
<script>
// The endpoint used by the ajax query - in this example it is same page.
const url=location.href; //http://localhost:5000/users/student_data
const create=function(t,a,p){
/*
utility to add a new node with attributes and content.
t=type ~ the DOM node type, defaults to DIV
a=attrbutes to set
p=parent to append new node to.
*/
let el=(typeof(t)=='undefined'||t==null)?document.createElement('div'):document.createElement(t);
let _arr=['innerHTML','innerText','html','text'];
for(let x in a)if(a.hasOwnProperty(x)&&!~_arr.indexOf(x))el.setAttribute(x,a[x]);
if(a.hasOwnProperty('innerHTML')||a.hasOwnProperty('html'))el.innerHTML=a.innerHTML||a.html;
if(a.hasOwnProperty('innerText')||a.hasOwnProperty('text'))el.innerText=a.innerText||a.text;
if(p!=null )typeof(p)=='object'?p.appendChild(el):document.getElementById(p).appendChild(el);
return el;
};
async function loadTable() {
try {
// It was not clear where dbTable was defined - hence declared here
const dpTable=document.querySelector('table#data');
while( dpTable.firstChild ) dpTable.removeChild( dpTable.firstChild );
// As this example targets the same page I needed a way to return only db results - hence using querystring.
const result = await fetch( url + '?fetch=data' );
const json = await result.json();
// the callback that will process the button clicks. The button has a dataset attribute set
// that will use the res.id value from each JSON record.
const clickhandler=async function(e){
if( window.confirm('Delete') ) {
const jsonReq = {
id:this.dataset.id
};
const result = await fetch( url, {
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify( jsonReq ),
});
console.log( result );
alert('Record deleted');
window.location.reload();
}
};
json.forEach( res => {
// find all the keys from the JSON ~ one of which will be the ID for the record ( res.id )
let fields=Object.keys( res );
// generate new table row
let tr=dpTable.insertRow();
// iterate through all json keys - add new table cell with JSON data.
fields.forEach( field => {
create('td',{
'data-id':field,
text:res[field]
},tr);
});
// add the button for the row and assign listener - also assign the dataset attribute for the record ID.
let bttn=create('button',{ text:'Delete','data-id':res.id }, tr.insertCell() );
bttn.addEventListener( 'click', clickhandler );
// add the icon to the button
create('i',{ 'class':'fas fa-trash trash-btn' }, bttn );
});
} catch(e) {
console.log('Unable to load list: %o',e);
}
}
document.addEventListener('DOMContentLoaded',loadTable);
</script>
</head>
<body>
<table id='data'></table>
</body>
</html>
The sql query shown here in the demo returns the following data:
[
{"id":"1","tag":"3D Archery","description":"a form of archery in which the goal is to strike targets in the shape of animals at unknown distances.","enabled":"1"},
{"id":"2","tag":"3x3","description":"a variation of basketball played on a half court with just three players in each team.","enabled":"1"},
{"id":"3","tag":"Abseiling","description":"an adventure sport where the participants descend a steep formation using a rope. (not really a sport).","enabled":"1"},
{"id":"4","tag":"Acroski","description":"athletes on snow skis perform various choreographed\u00c2\u00a0routines (once called Ski Ballet)","enabled":"1"},
{"id":"5","tag":"Adventure Racing","description":"an event combining two or more endurance disciplines, such as Orienteering, Cross-Country Running, Mountain Biking, Paddling and Climbing. It is also called Expedition Racing.","enabled":"1"},
{"id":"6","tag":"Aerials","description":"a freestyle skiing discipline in which athletes ski along a take-off ramp, then perform various in-air tricks.","enabled":"1"},
{"id":"7","tag":"Aerobatics","description":"sport aerobatics involves aircraft maneuvers such as rolls, loops, stall turns (hammerheads), and tailslides.","enabled":"1"},
{"id":"8","tag":"Acrobatic Gymnastics","description":"team of gymnasts work together to perform acrobatic moves in combination with dance moves.","enabled":"1"},
{"id":"9","tag":"Aerobic Gymnastics","description":"another name for Sport Aerobics.","enabled":"1"},{"id":"10","tag":"Aeromodeling","description":"activity using remotely controlled flying model aircraft (not really a sport).","enabled":"1"}
]
And yielded the following output:
I have this keyboard site launcher script, which I copied from some place years ago and it works fine as is. I want to enhance it by adding a cascading keypress launch for some of the keys. Here is my code:
<html><head>
<script language="JavaScript">
<!-- Begin
var key = new Array();
key['a'] = "https://www.arstechnica.com";
key['g'] = "https://www.google.com";
key['s'] = "https://slashdot.org";
key['y'] = "http://www.yahoo.com";
function getKey(keyStroke) {
isNetscape=(document.layers);
eventChooser = (isNetscape) ? keyStroke.which : event.keyCode;
which = String.fromCharCode(eventChooser).toLowerCase();
// alert('['+which+'] key \n has been stroke');
runUrl(which);
}
function runUrl(which) {
for (var i in key)
if (which == i) {window.location = key[i];}
}
document.onkeypress = getKey;
// End -->
</script></head>
<body>
Make a selection<br>
<br>
key['a'] = "https://www.arstechnica.com";
key['g'] = "https://www.google.com";
key['s'] = "https://slashdot.org";
key['y'] = "http://www.yahoo.com";
<br>
<br>
<!-- I solemnly swear this page is coded with vi or notepad.exe depending on the OS being used -->
</body>
</html>
Now, I want to modify the action for pressing the letter "s" to launch a submenu of sorts and ask me to select if I want to go to "Slashdot" or Spotify" for instance. like if I press an "s" second time, it goes to slashdot and if I press "f" for instance, it goes to spotify.
My problem is, I have never programmed in Javascript other than copying and pasting code and changing string values in the code, like here, changing the pressed keys and site URLs.
Any pointers, regarding how to start modifying this code, are greatly appreciated.
to be honest, the code provided is a bit outdated but I keep it so you can see the necessary changes that I made for the menu to be added and to implement the feature it's just a sketch but I will do the job I think from here you can expand, hope this puts you in the right direction
let isopenMenu = true;
const menu = document.getElementById("menu");
function toggleMenu() {
isopenMenu = !isopenMenu;
menu.style.display = isopenMenu ? "block" : "none";
}
var key = new Array();
key["a"] = "https://www.arstechnica.com";
key["g"] = "https://www.google.com";
key["s"] = "https://slashdot.org";
key["y"] = "http://www.yahoo.com";
key["b"] = "http://www.stackoverflow.com";
key["c"] = "http://www.test.com";
const menuSite = ["b", "c", "s"];
function getKey(keyStroke) {
isNetscape = document.layers;
eventChooser = isNetscape ? keyStroke.which : event.keyCode;
which = String.fromCharCode(eventChooser).toLowerCase();
runUrl(which);
}
function runUrl(which) {
for (var i in key)
if (which == i) {
if (which === "s") {
return toggleMenu();
}
if (!isopenMenu && menuSite.includes(which)) {
return;
}
window.location = key[i];
}
}
document.onkeypress = getKey;
window.addEventListener("load", toggleMenu);
<html><head>
<script language="JavaScript">
</script></head>
<body>
Make a selection<br>
<br>
key['a'] = "https://www.arstechnica.com";
key['g'] = "https://www.google.com";
key['s'] = "to toggel menu
key['y'] = "http://www.yahoo.com";
<br>
<br>
<ul id="menu">
<li>key['b'] = "http://www.stackoverflow.com";</li>
<li>key['c'] = "http://www.test.com</li>
</ul>
</body>
</html>
Indeed the code you've provided seems a bit dusted. There's some stuff that isn't done in that way nowadays. Notepad is an editor I still occassionally use though.
Since you've mentioned that you never really used JavaScript it's a bit hard to give you advice. You can do things way more elegant and even improve the look - but I'd say this would just confuse you even more. So let's work on something based on your code.
At the moment the keys and the corresponding targets are stored in an object (yeah, it's an object not an array). We can use a second object - let' say subKey - to store the additional targets upon pressing s.
var key = {};
key.a = "https://www.arstechnica.com";
key.g = "https://www.google.com";
key.s = "subMenu";
key.y = "http://www.yahoo.com";
var subKey = {};
subKey.a = "https://www.stackoverflow.com";
subKey.g = "https://www.startpage.com";
subKey.s = "goBack";
As you can see I've reserved the key s to go to the sub menu and inside the sub menu this button is used to go back to the main menu.
Now instead of hardcoding what the user gets to see on screen, we can iterate over those objects and use the information from there. To do this we need to reserve a html element - I've chosen an empty <div> which acts as some sort of container. As we iterate over the object we construct a string with the keys and it's associated targets and ultimately assign this this to the div's .innerHTML property.
let container = document.getElementById("container");
container.innerHTML = "Make a selection<br><br>";
for (var i in obj) {
container.innerHTML += "key['" + i + "'] = " + obj[i] + "<br>";
}
As the procedure is the same for both objects we just need to wrap it inside a function and pass it a reference to the desired object.
Your runUrl function needs to be modified a bit to take care of the additional options. This is best done with a simple if-else construct. So in pseudo-code:
if choice is subMenu open sub menu
if choice is goBack open main menu
if it's none of the above open a link
If we put everything together, your example looks a little bit like this:
(Just click on 'Run code snippet' and make sure to click somewhere inside the window so it'll have key focus)
<html>
<head>
</head>
<body>
<div id="container">
</div>
</body>
<script type="text/javascript">
var key = {};
key.a = "https://www.arstechnica.com";
key.g = "https://www.google.com";
key.s = "subMenu";
key.y = "http://www.yahoo.com";
var subKey = {};
subKey.a = "https://www.stackoverflow.com";
subKey.g = "https://www.startpage.com";
subKey.s = "goBack";
var currentObj = key;
function getKey(event) {
let which = String.fromCharCode(event.keyCode).toLowerCase();
runUrl(which)
}
function runUrl(which) {
for (var i in currentObj) {
if (which == i) {
if (currentObj[i] != "subMenu") {
if (currentObj[i] != "goBack") {
window.location = currentObj[i];
} else {
populateMenu(key);
}
} else {
populateMenu(subKey);
}
}
}
}
function populateMenu(obj) {
currentObj = obj;
let container = document.getElementById("container");
container.innerHTML = "Make a selection<br><br>";
for (var i in obj) {
container.innerHTML += "key['" + i + "'] = " + obj[i] + "<br>";
}
}
populateMenu(key);
document.onkeypress = getKey;
</script>
</html>
It looks like could achieve this with arbitrary list of sites. If so, you could handle this a little more generically by providing a list of sites and filtering the sites based on keystrokes.
If so, you can achieve it with the following:
const sites = [
'https://www.arstechnica.com',
'https://www.google.com',
'https://mail.google.com',
'https://slashdot.org',
'https://spotify.com',
'http://www.yahoo.com',
];
let matches = sites;
document.getElementById('keys').addEventListener('keyup', event => {
const keys = event.target.value.toLowerCase().split('');
matches = sites
.map(site => ({ site, stripped: site.replace(/^https?:\/\/(www\.)?/i, '')})) // strip out https://wwww. prefix
.filter(site => isMatch(site.stripped, keys))
.map(site => site.site);
if (event.keyCode === 13) {
if (matches.length === 0) {
alert('No matches');
} else if (matches.length === 1) {
alert(`launching ${matches[0]}`);
} else {
alert('More than one match found');
}
matches = sites;
}
document.getElementById('matches').textContent = matches.join(', ');
});
// find sites matching keys
function isMatch(site, keys) {
if (keys.length === 0) return true;
if (site.indexOf(keys[0]) !== 0) return false;
let startIndex = 1;
for (let i = 1; i < keys.length; i++) {
let index = site.indexOf(keys[i], startIndex);
if (index === -1) return false;
startIndex = index + 1;
}
return true;
}
document.getElementById('matches').textContent = matches.join(', ');
<div>Keys: <input type="text" id="keys" autocomplete="off" /> press Enter to launch.</div>
<p>Matches: <span id="matches" /></p>
The key parts to this are:
Define a list of sites you want to handle
Ignore the the https://wwww prefixes which is achieved with site.replace(/^https?:\/\/(www\.)?/i, '')
Implement filter logic (in this case it is the isMatch method) which tries to match multiple keystrokes
For demonstration purposes, I've wired keyup to an input field instead of document so that you can see it in action, and the action is triggered with the enter/return key.
I am instantiating a new variable from a class. The class has one constructor, city and then fetches jazz clubs through the foursquare API.
When I hard-code the city name into the instantiated class, it works fine. But when I want to feed it a dynamic value (a query from the search bar which I grab through the DOM), it won't recognise the city. Here is the code:
The Class:
class Venues {
constructor(city) {
this.id = '...';
this.secret = '...';
this.city = city;
}
async getVenues() {
const response = await fetch(`https://api.foursquare.com/v2/venues/search?near=${this.city}&categoryId=4bf58dd8d48988d1e7931735&client_id=${this.id}&client_secret=${this.secret}&v=20190309`);
const venues = await response.json();
return venues;
}
}
const input = document.getElementById('search-input').value;
const button = document.getElementById('button');
const jazzClubs = new Venues(input);
button.addEventListener('click', (e) => {
e.preventDefault();
getJazzVenues();
})
function getJazzVenues() {
jazzClubs.getVenues()
.then(venues => {
console.log(venues);
})
.catch(err => {
console.log(err);
});
}
Anyone knows why the the input variable's value is not recognised by the newly instantiated jazzClubs variable?
Also, if you have tips on how to structure this code better or neater, I'd welcome any suggestions (the class definition is in a separate file already).
Many thanks guys!
Adam
You need to make sure, the following statements are triggered after the button click.
const input = document.getElementById('search-input').value;
const jazzClubs = new Venues(input);
Also your code looks too complex. Use simpler code using jquery.
Try something like this:
$(document).ready(function() {
$("#search-button").click(function() {
var searchval = $("#search-input").val();
var id = "xxx";
var secret = "yyy";
alert(searchval);
var url = "https://api.foursquare.com/v2/venues/search?near=" + searchval + "&categoryId=4bf58dd8d48988d1e7931735&client_id=" + id + "&client_secret=" + secret + "&v=20190309";
alert(url);
$.ajax({
url: url,
dataType: 'json',
success: function(data) {
var venues = data.response.venues;
alert(venues);
$.each(venues, function(i, venue) {
$('#venue-result').append(venue.name + '<br />');
});
}
});
});
});
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<label>City:</label>
<input type="text" id="search-input" />
<button type="button" id="search-button">Search</button>
<div id="venue-result"></div>
</body>
</html>
I have a problem with javascript. I use google api and it contains ajax. The problem here is that, I need to catch values from URL like http://examplesite.com/index.php?s=some+values . I need to search values automatically. I try to do this for along time. However, I couldn't. How can I do this ?
This is my submit form:
<form id="searchForm" method="post">
<fieldset style="width: 520; height: 68">
<input id="s" type="text" name="s" />
<input type="submit" value="Submit" id="submitButton" />
Here is my javascript codes:
$(document).ready(function(){
var config = {
siteURL : 'stackoverflow.com', // Change this to your site
searchSite : true,
type : 'web',
append : false,
perPage : 8, // A maximum of 8 is allowed by Google
page : 0 // The start page
}
// The small arrow that marks the active search icon:
var arrow = $('<span>',{className:'arrow'}).appendTo('ul.icons');
$('ul.icons li').click(function(){
var el = $(this);
if(el.hasClass('active')){
// The icon is already active, exit
return false;
}
el.siblings().removeClass('active');
el.addClass('active');
// Move the arrow below this icon
arrow.stop().animate({
left : el.position().left,
marginLeft : (el.width()/2)-4
});
// Set the search type
config.type = el.attr('data-searchType');
$('#more').fadeOut();
});
// Adding the site domain as a label for the first radio button:
$('#siteNameLabel').append(' '+config.siteURL);
// Marking the Search tutorialzine.com radio as active:
$('#searchSite').click();
// Marking the web search icon as active:
$('li.web').click();
// Focusing the input text box:
$('#s').focus();
$('#searchForm').submit(function(){
googleSearch();
return false;
});
$('#searchSite,#searchWeb').change(function(){
// Listening for a click on one of the radio buttons.
// config.searchSite is either true or false.
config.searchSite = this.id == 'searchSite';
});
function googleSearch(settings){
// If no parameters are supplied to the function,
// it takes its defaults from the config object above:
settings = $.extend({},config,settings);
settings.term = settings.term || $('#s').val();
if(settings.searchSite){
// Using the Google site:example.com to limit the search to a
// specific domain:
settings.term = 'site:'+settings.siteURL+' '+settings.term;
}
// URL of Google's AJAX search API
var apiURL = 'http://ajax.googleapis.com/ajax/services/search/'+settings.type+'?v=1.0&callback=?';
var resultsDiv = $('#resultsDiv');
$.getJSON(apiURL,{q:settings.term,rsz:settings.perPage,start:settings.page*settings.perPage},function(r){
var results = r.responseData.results;
$('#more').remove();
if(results.length){
// If results were returned, add them to a pageContainer div,
// after which append them to the #resultsDiv:
var pageContainer = $('<div>',{className:'pageContainer'});
for(var i=0;i<results.length;i++){
// Creating a new result object and firing its toString method:
pageContainer.append(new result(results[i]) + '');
}
if(!settings.append){
// This is executed when running a new search,
// instead of clicking on the More button:
resultsDiv.empty();
}
pageContainer.append('<div class="clear"></div>')
.hide().appendTo(resultsDiv)
.fadeIn('slow');
var cursor = r.responseData.cursor;
// Checking if there are more pages with results,
// and deciding whether to show the More button:
if( +cursor.estimatedResultCount > (settings.page+1)*settings.perPage){
$('<div>',{id:'more'}).appendTo(resultsDiv).click(function(){
googleSearch({append:true,page:settings.page+1});
$(this).fadeOut();
});
}
}
else {
// No results were found for this search.
resultsDiv.empty();
$('<p>',{className:'notFound',html:'No Results Were Found!'}).hide().appendTo(resultsDiv).fadeIn();
}
});
}
function result(r){
// This is class definition. Object of this class are created for
// each result. The markup is generated by the .toString() method.
var arr = [];
// GsearchResultClass is passed by the google API
switch(r.GsearchResultClass){
case 'GwebSearch':
arr = [
'<div class="webResult">',
'<h2>',r.title,'</h2>',
'<p>',r.content,'</p>',
'',r.visibleUrl,'',
'</div>'
];
}
// The toString method.
this.toString = function(){
return arr.join('');
}
}
});
Look at my answer here. As you can see, it is not too difficult to set a get parameter. Now, I will show you how you can get a get parameter:
function getGetParameter(paramName)
{
var url = window.location.href;
if (url.indexOf(paramName + "=") >= 0)
{
var returnValue = url.substring(url.indexOf(paramName + "="));
if (returnValue.indexOf("&") >= 0)
{
returnValue = returnValue.substring(0, returnValue.indexOf("&"));
}
return returnValue.substring(returnValue.indexOf("=") + 1);
}
return null;
}
As about searching for values automatically, you need to specify what and how would you like to search for, as this can be needed/done literally in infinitely many ways.
maybe this is the problem: you're trying to use an API and it's no longer avaiable.
Object {responseData: null, responseDetails: "This API is no longer available.", responseStatus: 403}
More information here: https://developers.google.com/image-search/v1/jsondevguide
Now, I'm trying to find a migration to version 2.
I am using Javascript to display headers on a SharePoint site, according to a column that I have specified HTML in. Here is the HTML.
<DIV style ='text-align:center; font-weight:bold; font-size: 20px;'>◆</DIV>
The problem is that the script, while rendering the HTML properly within the page, does not do the same for header. It works 90% of the way, but instead of displaying the unicode "◆", it renders "â".
I've already tried modifying the Javascript to try to account for the unicode \u25c6, but I'm failing miserably. Can anyone help me out or provide me some clues as to why this is happening?
Here is the Javascript.
<script type="text/javascript">
// Find all Web Parts in the page
var listWP = [],
calWP = [],
divs = document.getElementById("MSO_ContentTable").getElementsByTagName("div");
var count=divs.length;
for (i=0;i<count;i++) {
try {
if (divs[i].id.indexOf("WebPartWPQ")==0){
if (divs[i].innerHTML.indexOf("ViewDefault_CalendarView")>=0) {
// Calendars
calWP.push(divs[i].id);
} else {
// Other Web Parts
listWP.push(divs[i].id);
}
}
}
catch(e){}
}
function TextToHTML(NodeSet, HTMLregexp) {
var CellContent = "";
var i=0;
while (i < NodeSet.length){
try {
CellContent = NodeSet[i].innerText || NodeSet[i].textContent;
if (HTMLregexp.test(CellContent)) {
NodeSet[i].innerHTML = CellContent;
}
}
catch(err){}
i=i+1;
}
}
var regexpA = new RegExp("\\s*<([a-zA-Z]*)(.|\\s)*/\\1?>\\s*$");
var regexpTD = new RegExp("^\\s*<([a-zA-Z]*)(.|\\s)*/\\1?>\\s*$");
var WP = new Object;
function UpdateWP() {
if (calWP.length>0){
for (i=0;i<calWP.length;i++) {
WP=document.getElementById(calWP[i]);
if (WP.innerHTML.indexOf("<\;")>=0) {
TextToHTML(WP.getElementsByTagName ("a"),regexpA);
}
}
}
if (listWP.length>0){
for (i=0;i<listWP.length;i++) {
WP=document.getElementById(listWP[i]);
if (WP.innerHTML.indexOf("<\;")>=0) {
TextToHTML(WP.getElementsByTagName ("td"),regexpTD);
}
}
}
// Check every 200000000 ms, forever
setTimeout("UpdateWP()",200000000);
}
UpdateWP();
function HeaderToHTML(){
var headers=document.getElementById("MSO_ContentTable").getElementsByTagName("li");
var regexpTR1 = new RegExp("FilterValue1=([\\S\\s]*)'\\)");
var regexpTR2 = new RegExp("FilterValue2=([\\S\\s]*)'\\)");
for (i=0;i<headers.length;i++) {
try{
var sp=headers[i].getElementsByTagName("span");
for (j=0;j<sp.length;j++) {
var test = sp[j].innerText || sp[j].textContent || " ";
//var test = sp[j].innerText;
if (test.indexOf("...")>0) {
//alert(test);
//var value = regexpTR1.exec(headers[i].innerHTML)[1];
var inner = headers[i].innerHTML;
var value = (inner.indexOf("FilterValue2")>0) ? regexpTR2.exec(headers[i].innerHTML) [1] : regexpTR1.exec(headers[i].innerHTML)[1];
//alert(value);
//alert(value.replace(/\\u00/g,"\%"));
value=value.replace(/\\u00/g,"%");
sp[j].innerHTML=unescape(unescape(value)).replace(/8_/," ");
}
}
}catch(e){}
}
}
setInterval(function(){HeaderToHTML();},100);
</script>
I would suggest using the html dex/dec for the symbols.
that is,
◆ = ◆
◆ = ◆
Wikipedia has a nice list of them broken into categories here.
I found the black diamond you're trying to write here
I think a solution could be to render your character in its encoding and let browser know about it via:
<meta http-equiv="Content-Type" content="text/html; charset="...">
For example, if you are using UTF-8:
<meta http-equiv="Content-Type" content="text/html; charset="UTF-8">