Overall Question First then please read: How can I remove the SelectedBusStops and SelectedBusRoutes and Zoom to map:map when user clicks home button?
Here is what I am trying to do. I have a JavaScript Application that utilizes that ArcGIS API for JavaScript along with some Ajax and JQuery in there. I'm trying to customize the Home Button or create my own, that can do more than simply zoom to the initial extent. I tried doing this on my own by creating a button that called a function goHome(). Then within that function I did things like remove the highlighted Selected route from the list and then scrolled to the top of the route list and removed any bus arrival times that may have been populate when a user clicked on a bus stop to show times. I've got it to do everything I want so far except remove the current highlighted (Selected Route) along with the Current Bus Stops that are associated with the selected Route and then Zoom to my initial map extent.
I was trying to do something like setting the setDefinitionExpression of the feature layer I wanted to modify, but due to where this function is in my code it cannot see them. When I put this function inside of the main function it gives me the ReferenceError: goHome is not defined. So I put it outside of the main function and everything works so far but can't figure out how to get the featureLayers to go away. It will give me an error saying:
ReferenceError: selectedBusStops is not defined
selectedBusStops.setDefinitionExpression("Route is null");
When a user clicks on a route in the list it will then go in and set a definitionExpression to show only that Selected Route. Like this:
selectedBusStops.setDefinitionExpression("Route like '%" + routeSlice + "%'");
Here are my FeatureLayers: ** Here was my issues with not being able to use these in another function. I declared these with the 'var' and that made them local to the function they were in. Removed the 'var' and problem solved, I can now at least clear the features. **
var selectedBusRoute = new FeatureLayer("http://PROD_RTC_SystemMap/MapServer/4", {
mode: FeatureLayer.MODE_SELECTION,
outFields: ["*"],
infoTemplate: selectedBusRouteTemplate
});
var selectedBusStops = new FeatureLayer("http://PROD_RTC_SystemMap/MapServer/0", {
mode: FeatureLayer.MODE_ONDEMAND,
outFields: ["*"],
infoTemplate: selectedBusStopsTemplate
});
I was thinking if there was a way to manipulate the esri/dijit/HomeButton somehow to do all of this or am I on the right track? Esri/github/homebutton
Thanks for taking the time to read this and I appreciate all input (even those that say my code looks like a*#).
Here is the code I have for the function that is getting called:
function goHome() {
// Reset the Accordian Content Panes and fix naming
var newTitle = "Bus Arrival Times";
dijit.byId("pane3").set("title", newTitle);
var container = dijit.byId("container");
container.selectChild("RTCBusroutes", true);
// This will remove old values in Bus Arrival Time pane so Bus Arrival Times will clear
var results = document.getElementById("results");
var rowCount = results.rows.length;
for (var x = rowCount - 1; x > 0; x--) {
results.deleteRow(x);
}
// Removes the Highlight from the currently selected Item in the RTC Bus Routes List pane
$('.highlight').removeClass('highlight');
// Scrolls to the top of the List in the RTC Bus Routes Content Pane List
$('#RTCBusroutes').scrollTop(0);
// Remove Currently Selected Bus Routes
// Remove Currently Selectes Bus Stops
// Zoom to Map Extent
}
Here is the Button I added:
<button id="homeImg" onclick="goHome()">
<img id="myImg" src="img/home.png" />
</button>
CSS for the Home Button #homeImg
/* for the Home Button CSS */
#homeImg {
position: absolute;
top: 138px;
left: 28px;
z-index: 8;
background-color: #888888;
opacity: 0.8;
filter: alpha(opacity=80);
cursor: pointer;
}
You can clear a featureLayer by calling the .clear() method on it, then smwill clear all graphics in the layer.
If you only want to clear the selections call the .clearSelections() method.
There is more documenta ion here on other featurelayer methods:
https://developers.arcgis.com/javascript/jsapi/featurelayer-amd.html#methods
Related
I'm trying to trigger some functionality based on the click of a marker on a GeoJSON layer in Leaflet. The eventual functionality I'm trying to implement is a flyout, or scroll out type modal populated from the individual feature's JSON attributes. Essentially, I'm trying to implement the functionality in this Tutsplus Tutorial with dynamic feature content based on the marker click.
I THINK I've figured out most of the pieces I need, but I'm struggling with how to add a data attribute, specifically data-open, to the individual marker. Building on an earlier question of mine I've realized it's not enough to just update a DOM element's CSS, but rather my app should be implementing changes based on data attributes to fully get the functionality I want.
From this question I know that this should be done by extending the L.Icon class that Leaflet provides, but the answer is a bit too terse for my current JS skills. I apologize for this effectively being a "ELI5" of a previously asked question, but I'm not sure where the options and slug come into function. I think they're implied by the question, rather than the answer I'm citing and being set on the marker itself.
Here's a simplified version of the the click handler on my markers, which grabs and zooms to location, gets feature info, and populates that info to a div. The zoom functionality works, as does extracting and placing the feature info, but I'm struggling with how to connect the functionality to trigger the modal and place the div with the feature info over the map.
function zoomToFeature(e) {
var latLngs = [e.target.getLatLng()];
var markerBounds = L.latLngBounds(latLngs);
var street = e.target.feature.properties.str_addr;
document.getElementById('street').textContent = street;
mymap.fitBounds(markerBounds);
//where the modal trigger should be
document.getElementById('infoBox').classList.add('is-visible');
}
Here are the event listeners taken from the linked tutorial, which are currently not firing, but I have them working in a standalone implementation:
const openEls = document.querySelectorAll("[data-open]");
const closeEls = document.querySelectorAll("[data-close]");
const isVisible = "is-visible";
//this is the event I want to trigger on marker click
for (const el of openEls) {
el.addEventListener("click", function() {
const modalId = this.dataset.open;
console.log(this);
document.getElementById(modalId).classList.add(isVisible);
});
}
for (const el of closeEls) {
el.addEventListener("click", function() {
this.parentElement.parentElement.parentElement.classList.remove(isVisible);
});
}
document.addEventListener("click", e => {
if (e.target == document.querySelector(".modal.is-visible")) {
document.querySelector(".modal.is-visible").classList.remove(isVisible);
}
});
So, where I'm trying to get is that when my markers are clicked, the trigger the modal to appear over the map. So, I think I'm missing connecting the marker click event with the event that triggers the modal. I think what's missing is adding the data attribute to the markers, or some way chain the events without the data attributes. As there's no direct way to add an attribute to the markers, I try to add slug option on my circle markers:
var circleMarkerOptions = {
radius: 2,
weight: 1,
opacity: 1,
fillOpacity: 0.8,
slug: 'open',
}
and If I read the previously asked question's answer correctly, than extending the Icon Class this way should add a data-open attribute.
L.Icon.DataMarkup = L.Icon.extend({
_setIconStyles: function(img, name) {
L.Icon.prototype._setIconStyles.call(this, img, name);
if (options.slug) {
img.dataset.slug = options.slug;
}
}
});
A stripped down version of my code is here (thanks #ghybs). My full implementation pulls the markers from a PostGIS table. It's a bit hard to see in the Plunker, but this code adds my class to my modal, but doesn't trigger the functionality. It does trigger the visibility if the class is manually updated to modal.is-visible, but the current implementation which renders modal is-visbile doesn't, which I think is because the CSS is interpreted on page load(?) and not in response to the update via the dev tools, while the concatenated css class matches extactly(?). When I do trigger the modal via the dev tools, the close modal listeners don't seem to work, so I'm also missing that piece of the puzzle.
So, it's a work-around to setting the data attribute, but I realized I was shoe-horning a solution where it wasn't needed. Assuming someone ends up with the same mental block. Appropriate listeners on the modal close button and another function passed to the existing marker click listener produce the desired functionality.
const closeM = document.querySelector(".close-modal");
closeM.addEventListener("click", closeMe);
var modal = document.getElementById('infoBox');
and
function modalAction(){
modal.style.display = 'block';
}
function closeMe(){
modal.style.display = 'none';
}
I'm making a slideshow with full screen functionality (FancyBox 3), it's using the RequestFullScreen method on the container div, which means any other element in the body is not accessible in full screen mode. (correct me if i'm wrong)
I would like to include an AddThis Expanding Share button in the slideshow, but because it's created dynamically by the AddThis js, it appends the Smart Layers before the end of the body, not at the end of the slideshow container div therefore it's not included in the full screen slideshow.
I couldn't find any info on Smart Layers DOM placement in the AddThis API.
What I've tried is seems like a bad idea, to manually appendTo the necessary divs to the slideshow container after the divs are created by AddThis, I managed to "cut and paste" the '.at-expanding-share-button' and it's working so far, but I can't "catch" the '#at-expanded-menu-host' (which is the layer created by AddThis for more sharing options, with the dark background), and I'm not even sure if this method will work properly...
Any help would be appreciated.
I figured it out! :) I thought I share my experience/solution, if anyone has similar difficulties.
What won't work and why:
First I've tried to communicate with the AddThis API to tell it to put its layers to a premade container, which looks like impossible and the AddThis team also told me that there is no solution for manual layer DOM placement, it's always appending those to the end of the body (at least for now, maybe they will implement this option into their API).
Then I've tried to manually append those layers to the Fancy-Box container, which was a dead end because when the slideshow closes, it removes its container from the markup, so the AddThis layers disappeared and I couldn't reinit it (maybe others have some solution for that, but I just couldn't figure it out).
By the way, the "More Sharing Options" layer is created when the share + button is clicked.
My solution:
Instead of appending the layers to the dynamic slideshow container, I've created a static div at the end of the body, appended the layers to it when they are created, and set the Fancy-Box parent div to my container (note that Fancy-Box full screen functionality makes its own div into full screen, so I had to use my own full screen function for the container with the layers and the slideshow).
I've used sindresorhus's screenfull for easier/cross-browser full screen functions.
var FullScreenContainer = $('#container');
// Check if AddThis Layers are ready
var checkLayers = setInterval(function() { appendLayers(); }, 1000);
// Append the layers to FullScreenContainer
function appendLayers() {
var layers = $('.at-expanding-share-button, #_atssh');
if(layers.length > 0){
addthis.layers.refresh();
layers.appendTo(FullScreenContainer);
clearInterval(checkLayers);
console.log('layers added');
}
else {
console.log('not found')
}
}
// Check for more layers when the share icon clicked
$(document).on('click', ".at-expanding-share-button-toggle", function() {
var checkMoreLayers = setInterval(function() { catchLayers(); }, 1000);
function catchLayers() {
var morelayers = $('#at-expanded-menu-host');
if(morelayers.length > 0){
morelayers.appendTo(FullScreenContainer);
clearInterval(checkMoreLayers);
console.log('more layers added');
}
else {
console.log('did not found more')
}
}
});
// Don't forget to disable the full screen function in Fancy-Box,
// then call them when necessary (onInit, clickSlide, clickOutside
// and the close button)
function enterFullscreen() {
screenfull.request($('#container')[0]);
}
function exitFullscreen() {
if (screenfull.isFullscreen) {
screenfull.exit();
$.fancybox.getInstance().close();
}
if (!screenfull.isFullscreen) {
$.fancybox.getInstance().close();
}
}
And you are good to go. I hope this helps for anybody else! :)
I am using bootstrap colorpicker in my logic as given below : -
for (var i = 0; i < abc.length; i++) {
if (selectedValue == abc[i].name) {
var color = getColor(selectedValue);
$('.colorPicker').colorpicker('setValue', color).on(
'changeColor', function(ev) {
var colorCode = ev.color.toHex();
changeColor(colorCode);
}).on('hidePicker', function(ev) {
alert("Hiding colorpicker");
});
}
}
The above code gets called from a method everytime a user select some value in dropdown.
Problem :
Now when I open the colorpicker and close it by defocusing it. I get the alert "Hiding colorpicker" once in first go.
If I select another value from dropdown that fires the above given code again and then I open and close the color picker again. The I get the alert twice and so on in increasing numbers.
My Understanding:
My understanding is that I am initializing color picker with different values everytime the above code is fired ,So code is creating different instances of colorpicker on same ".colorPicker" class element. So I need to remove the previously created instance of colorPicker before initializing a new one.
What I tried:
I used the below given line as the first line in my method to destroy this first. But it's not working.
$('.colorPicker').colorpicker('destroy');
Any help that how can I destroy the colorpicker everytime and then use the new instance on every method call so that I can avoid multiple alerts of "Hiding colorpicker".
I have a single paged website, in which i've got a div named sitecontent with the width of 4400, which holds 4 "pages". The pages start at 0px of sitecontent, 1100 px of sitecontent, 2200px of sitecontent, and 3300px.
I use Jquery to set de div position to the right px, so i get the right text displayed. After pressing a link i get for example:
<div id="site-content" style="left: -1100px;">
At one of the pages i have to refresh the page, and after this refresh i want the page to display the same "page" on 1100px, but it starts at 0px, the home page.
Is there any way how i can make sure that the sitecontent starts at -1100px of home?
Thanks in advance,
Cheers
You need to append some identifier onto the hash of the URL that you can parse on the page load.
For example:
http://www.somewebpage.com/somepage#page1
Then in the load of the page, you can inspect this hash value and immediately change the UI to show the new page:
var hash = window.location.hash;
if(hash == "#page1")
$('#site-content').css('left', '-1100px');
You can use a cookie to store the value, then, every time the page loads, you need to check the cookie and deal with the value collected:
The link for Jquery Cookie with download and usage manual!
HTML (example)
<a href="#" title="Go Page 1" id="page_01" class="setCookie">
Click to view page 01
</a>
JQUERY (jquery.cookie)
// SET THE COOKIE
$('.setCookie').bind("click", function() {
var pageNumber = $(this).attr("id");
$.cookie('the_cookie_name', pageNumber, { expires: 7, path: '/' });
});
// READ THE COOKIE
$(function() {
var oldNumber = $.cookie('the_cookie_name');
if (oldNumber !== NULL) {
$("#page_"+oldNumber).trigger("click");
}
});
Note:
The link that you currently use to change pages, should have the class "setCookie" to trigger the cookie creation, and also the id that is being used to identify the page number.
One advantage of this is that you can control for how long is the cookie preserved and thus allowing the visitant to resume the website experience.
An approach very similar to what Tejs is proposing would be to use a hash-based routing framework that listens to hash changes. That will result in much cleaner code since you don't need to define the scrolling in two different places.
Every link in your page is currently being observed by a jQuery event listener (onclick -> moves the content container to the show the desired content). The HTML looks probably somewhat like this: Contact details.
With this approach, you don't need to watch those links. Instead, simply make them change the hash: Contact details.
Now observe the hash and react to changes. I'm using the Simrou framework (https://github.com/buero-fuer-ideen/Simrou) but you can go with any other framework that provides similar functionality.
jQuery(document).ready(function() {
// Define a function that moves the content, e.g. moveContent(3300);
function moveContent(pixelPosition) {
$('#site-content').css('left', '-' + pixelPosition + 'px');
}
// Setup the router
var router = new Simrou({
'page1': function() { moveContent(0); },
'page2': function() { moveContent(1100); },
'page3': function() { moveContent(2200); },
'page4': function() { moveContent(3300); }
});
router.start();
});
That's all the javascript you need!
(and here is a quick and dirty fiddle, demonstrating the whole thing: http://jsfiddle.net/R7F6r/)
How can i setup a callback function for a click event on a pin?
I need both pins the green one (one location) and the red one (clustered locations).
I'm using the v6 api.
That's the code so far:
var shape = new VEShape(VEShapeType.Pushpin, new VELatLong(pin.position.lat, pin.position.lng));
shape.SetTitle('<h2>'+pin.overlay.headline+'</h2>');
shape.SetDescription('<p>'+pin.overlay.text+'</p>');
var pinIcon = new VECustomIconSpecification();
pinIcon.Image = '/images/map/pin.png';
pinIcon.TextContent = '.';
shape.SetCustomIcon(pinIcon);
The correct way to do this is to override the onclick and onmouseover
functions within VEMap.AttachEvent(); AttachEvent has many return values
useful in identifying which pin you click/mouseover, such as the pinID (elementID return value for the VEMap.onclick() method).
You can use the ID in combination with map.ShowInfoBox(), and map.GetShapeByID to
show your infobox upon click.
READ: VEMap.AttachEvent <-- Google that, I'd paste the URL but I need more rep on StackOverflow
READ: VEMap.onclick. Also, Understand the return values:
READ All mouse events
http://msdn.microsoft.com/en-us/library/bb412438.aspx
/*************************************
* Code snippets..
* For example purposes I'm going to do
* things a bit out of order.
*************************************/
// Attach your events in the map load callback:
map.AttachEvent("onmouseover", myFnForMouseOver);
map.AttachEvent("onclick", myFnForOnClick);
// Callback functions used in the AttachEvent(s) above..
function myFnForMouseOver(e){
if(e.elementID){
return true; // Disables the rollover ShowInfoBox()
}
}
function myFnForOnClick(e){
if(e.elementID){
// Show infobox using the PINs/Shapes actual ID, (return value from e.elementID)
map.ShowInfoBox(map.GetShapeByID(e.elementID));
return true;
}
}
/*
* end
*/
Thats it
R Baker
You are not limited to setting just the text and image of a pin -- you can use the CustomHTML property to specify it as an HTML element. That allows you to handle clicks or any other events on it.
A simple example would have an HTML pin image with an inline click handler:
pinIcon.CustomHTML = "<img onclick='alert(\"tadaa\")' src='/images/map/pin.png' />";
If you are separating code from markup, e.g. using jQuery, you can specify the pin's element ID, and use that later to associate a click handler with it. For example:
pinIcon.CustomHTML = "<img id='pin' src='/images/map/pin.png' />";
shape.SetCustomIcon(pinIcon);
...
map.AddShape(shape);
$("#pin").click(function() { alert("tadaa"); });