I have some DOM elements that I don't directly have access to, as they are rendered from an API call. What I would like to do is add a class to this element once it's rendered on the DOM.
Using Template.rendered does not work, as the template renders properly before these DOM elements appear from the API.
My current solution is a Meteor.setTimeout—which might be the definition of a hack—and it only works about 90% of the time.
What is the best way to trigger a function when a particular DOM element is rendered?
Here is some of the relevant code from the API call:
Template.map.rendered = function() {
return this.autorun(function() {
var drawControl, drawnItems, mmap;
if (Mapbox.loaded()) {
L.mapbox.accessToken = '<API KEY>';
L.mapbox.config.FORCE_HTTPS = true;
mmap = L.mapbox.map('map', '<TOKEN>');
L.control.scale().addTo(mmap);
var featureGroup = L.featureGroup().addTo(mmap);
drawControl = new L.Control.Draw({
draw: {
polygon: false,
polyline: false,
rectangle: true,
circle: false,
marker: false
}
});
mmap.addControl(drawControl);
mmap.addControl(L.mapbox.geocoderControl('mapbox.places', {
autocomplete: true
}));
function showPolygonAreaEdited(e) {
e.layers.eachLayer(function(layer) {
showPolygonArea({ layer: layer });
});
}
function showPolygonArea(e) {
coords = {
lat1: normalizeLon(e.layer.toGeoJSON().geometry.coordinates[0][1][0]),
lon1: normalizeLat(e.layer.toGeoJSON().geometry.coordinates[0][1][1]),
lat2: normalizeLon(e.layer.toGeoJSON().geometry.coordinates[0][3][0]),
lon2: normalizeLat(e.layer.toGeoJSON().geometry.coordinates[0][3][1])
}
featureGroup.clearLayers();
featureGroup.addLayer(e.layer);
e.layer.openPopup();
}
mmap.on('draw:created', showPolygonArea);
mmap.on('draw:edited', showPolygonAreaEdited);
}
});
};
I've removed a lot of extraneous code, so this might not compile properly... But it has all the relevant bits.
The selector I initially tried to use was this:
Template.map.rendered = function() {
$('.leaflet-draw-section').attr('data-intro', 'Hello step one!')
...
...
But it didn't work, since the API elements hadn't rendered yet.
Looking at the map box api, it appears that L.mapbox.map does the rendering:
<script>
// Provide your access token
L.mapbox.accessToken = 'id1' //
// Create a map in the div #map
L.mapbox.map('map', 'id2');
// add your class to #map right here!!
</script>
If that doesnt work, them maybe L.mapbox.map is doing something asynchronous. They don't give you a callback, so a window.setTimout(func, 0) may be necessary
Make sure your code looks like this
Template.templatename.onRendered(function(){
//yourcode
});
The solution comes from the Mapbox API. There is a callback on "ready":
var layer = L.mapbox.tileLayer('mapbox.streets');
layer.on('ready', function() {
// the layer has been fully loaded now, and you can
// call .getTileJSON and investigate its properties
});
Related
I have a map with bingmap and I implemented waypoints. I would like when I click in a waypoint it displays an info window.
if (!$scope.directionsManager) { _createDirectionsManager(); }
if($scope.directionsManager.getAllWaypoints().length < 2)
{
$scope.directionsManager.resetDirections();
$scope.waypoints.forEach(function (waypoint) {
var order_waypoint = new Microsoft.Maps.Directions.Waypoint({ location: new Microsoft.Maps.Location(waypoint.lat, waypoint.lng) });
console.log("order_waypoint", order_waypoint)
$scope.directionsManager.addWaypoint(order_waypoint);
})
}
var renderOption = {
itineraryContainer: document.getElementById('directionsItinerary'),
waypointPushpinOptions:{
// icon: "http://vignette3.wikia.nocookie.net/freeciv/images/1/1c/Crystal_128_penguin.png/revision/latest?cb=20071106133132&path-prefix=es",
// hoverIcon: "http://vignette3.wikia.nocookie.net/freeciv/images/1/1c/Crystal_128_penguin.png/revision/latest?cb=20071106133132&path-prefix=es",
// height: 10,
// width: 10,
draggable: false,
textOffset: new Microsoft.Maps.Point(-1, 3)
}
}
$scope.directionsManager.setRenderOptions(renderOption);
$scope.directionsManager.calculateDirections();
Thanks!
I haven't used the Bing maps API, but the docs on the Waypoint class make it look like you might need a custom pushpin in order to intercept its click event.
var location = new Microsoft.Maps.Location(waypoint.lat, waypoint.lng);
var pushpin = new Microsoft.Maps.Pushpin(location);
Microsoft.Maps.Events.addHandler(pushpin, 'click', function() {
// Implementation can go here
});
var order_waypoint = new Microsoft.Maps.Directions.Waypoint(
{location: location, pushpin: pushpin});
$scope.directionsManager.addWaypoint(order_waypoint);
It is possible you can get a reference to the default pushpin on order_waypoint without creating your own custom one, and use addHandler to bind the click event to it. I don't have running code to test that on, and I don't see a way to get a reference to the default pushpin, only the custom one. You could try it anyway (order_waypoint.getPushpin()) and see if it works.
So, I'm building a map of water taking permits in Toronto. Work in progress available here: http://pennybeames.net/maps/PermitsTO.html
I'm using L.mapbox.featureLayer to add my markers:
var permitsLayer = L.mapbox.featureLayer(permits, {
pointToLayer: function (feature, latlng) {
return L.circleMarker(latlng, permitsStyle(feature));}
}).addTo(map);
I've got the custom info control from the Leaflet choropleth example working (http://leafletjs.com/examples/choropleth.html).
I change the style and update the info in the custom control as follows:
featureLayer.eachLayer(function (layer) {
layer.on('mousemove', function (e) {
// highlight feature
layer.setStyle({
weight: 2,
opacity: 1,
fillOpacity: 0.9
});
//update properties in DomUtil
info.update(layer.feature.properties);
});
});
All is working smoothly until we get to mouseout. The info control updates fine (either switching to the properties of the new target or reverting to its empty state), but the style won't change. The highlight stays on.
featureLayer.eachLayer(function (layer) {
layer.on('mouseout', function (e) {
//reset style
featureLayer.setStyle(permitsStyle);
//empty properties in DomUtil
info.update();
});
});
I don't get any errors, but I also don't see any change. I've tried
featureLayer.resetStyle(e.target)
but then all I get is Uncaught TypeError: featureLayer.resetStyle is not a function.
There is probably a really simple question, but it's eluding me. Insight, hivemind?
Don't bind to your events inside the eachLayer loop, just do featureLayer.on('myevent',function(e) {e.layer.setStyle(MY_NEW_STYLE)})
I'm trying to produce a mapping application with Bing Maps with a button that will retrieve a JSON string and places pins on the map based on the center of the map.
That is working fine, but I'm running into two issues that I'm having trouble diagnosing.
The first is that when I move the map after placing the pins, the majority of them disappear from the map except for 1-3. I've figured out that the pins are still being held in map.entities, but just aren't all displaying.
The second issue is that I have a click event on the pins, and sometimes when I click on a pin it will disappear (and sometimes reappear elsewhere on the map).
Here is my code:
function addPin() {
map.entities.clear();
var pinImg = "images/MapPin.jpg";
var latLong = {};
var name;
for (var i = 0; i < factualJson.response.data.length; ++i) {
latLong['latitude'] = factualJson.response.data[i].latitude;
latLong['longitude'] = factualJson.response.data[i].longitude;
name = factualJson.response.data[i].name;
var pin = new Microsoft.Maps.Pushpin(latLong, {
icon: pinImg,
anchor: new Microsoft.Maps.Point(latLong['latitude'], latLong['longitude']),
draggable: true,
width: 48,
height: 48
});
Microsoft.Maps.Events.addHandler(pin, 'click', displayName);
pin.title = name;
pin.id = 'pin' + i;
map.entities.push(pin);
}
document.getElementById("arrayLength").innerHTML = "Number of locations: " + map.entities.getLength();
}
function displayName(e) {
document.getElementById("name").innerHTML = "";
if (this.target.id != -1) {
document.getElementById("name").innerHTML = this.target.title;
}
}
function boot() {
Microsoft.Maps.loadModule('Microsoft.Maps.Overlays.Style', { callback: getMap });
}
function getMap() {
map = new Microsoft.Maps.Map($gel("bingMap"), {
credentials: getKey(),
customizeOverlays: true,
enableClickableLogo: true,
enableSearchLogo: true,
showDashboard: true,
showBreadcrumb: true,
showCopyright: true,
zoom: 10,
labelOverlay: Microsoft.Maps.LabelOverlay.hidden
});
setGeoLocation();
//setTimeout(optimizeMap, 100);
window.onresize = resizeWin;
resizeWin();
}
Currently I make an ajax call from the button, and the callback function calls 'AddPin' which adds the pins to the map. I thought I'd add in the map initialization code in case it was relevant. Currently boot() is called on body load.
For me the solution was similar to yours #canadian coder
Microsoft.Maps.Location() only accepts float values, no strings and Int.
I use MVC architecture and passed a string using a model. Later i converted that string to float and passed to Location.
Problem solved.
var pushpin = new Microsoft.Maps.Pushpin(
center, { icon: '/Content/BingPushpin.png', width: 50, height: 50, draggable: false });
pushpin.setLocation(new Microsoft.Maps.Location
(parseFloat(#Model.Latitude) , parseFloat(#Model.Longitude)));
dataLayer.push(pushpin);
locations.push(new Microsoft.Maps.Location
(parseFloat(#Model.Latitude) , parseFloat(#Model.Longitude)));
EDIT :
Later found out that problem still exist. Another reason can be that you are calling that Map to load twice. So check for any other instance of the map which is being loaded. In my case see below.
$(document).ready(function () {
loadSearchModule(); //calling Map to Load at a div without pushpins
//code to do something
getMapOnLocation();
}
function getMapOnLocation()
{//code to display map with pushpin
}
In the Above example I was telling the control to load my map with PushPins and when the page is fully Loaded load the map without pushpins.
Hope this helps :)
As always I need to ask the question before I figure it out myself.
The issue was that I needed to push a Microsoft location object into the pin and not an object. Like so:
var loc = new Microsoft.Maps.Location(47.592, -122.332);
And NOT my latLong object.
This also seemed to fix the issue of disappearing pins on click event.
I'm implementing an OpenLayers SelectFeature control, and trying to position an JQuery UI dialog widget right on top of the selected feature. To use the JQuery UI Position utility, it requires either a DOM element or an Event.
The onSelect callback of the SelectFeature control gives me an OpenLayers.Feature.Vector object representing the selected feature. From this, how do I get either the DOM element of the selected feature, or the Event object of the click event?
var selectControl = new OpenLayers.Control.SelectFeature(clientsLayer, {
hover : false,
clickout: false,
multiple: false,
onSelect: function(feature) {
// how do I get the DOM element of the feature
// or alternately, the click event of the selection?
}
});
You are doing it right.
If you do a console.log(feature) You'll see that it returns an object with CLASS_NAME =
"OpenLayers.Feature.Vector"
onSelect: function(feature) {
console.log(feature);
}
Update:
I see.
You could add event listeners
var selectControl = new OpenLayers.Control.SelectFeature(clientsLayer, {
hover: false,
clickout: false,
multiple: false,
eventListeners: {
featurehighlighted: function (event) {
console.log(event);
console.log(event.feature);
}
}
});
Is it something like this you look for ?
onSelect: function onFeatureSelect(event) {
var feature = event.feature;
if ( feature.layer.name == 'theone') {
...
}
}
Note I have also posted this answer at How do I get the DOM element from openlayers vector feature
If you want to find the position of the mouse or feature on hover so you can display a custom overlay, create a custom hover control and define the featurehighlighted function as follows:
var featureHoverControl = new OpenLayers.Control.SelectFeature([myLayer], {
id: 'featureHoverControl',
hover: true,
autoActivate: true,
highlightOnly: true,
renderIntent: "temporary",
eventListeners: {
featurehighlighted: function(e) {
// either use the mouse's position when the event was triggered
var mouseXPosition = this.handlers.feature.evt.x;
var mouseYPosition = this.handlers.feature.evt.y;
// or retrieve the feature's center point
var featureCenterLonLat = e.feature.geometry.bounds.getCenterLonLat();
var featureCenterPoint = map.getPixelFromLonLat(featureCenterLonLat);
// display your custom popup here
// e.g. showTooltip(e.feature.attributes.name, featureCenterPoint.x, featureCenterPoint.y)
}
,
featureunhighlighted: function(e) {
// hide your custom popup here
// e.g. hideTooltip()
}
}
});
map.addControl(featureHoverControl);
If you require access to the SVG element representing your layer/feature (in the event you are using a third-party library and you don't feel like modifying the source code), use either of the following lines (depending if you require the layer or feature):
var layerElement = map.getLayersByName("My Layer")[0].features.root;
var layerElementId = map.getLayersByName("My Layer")[0].features.root.id;
var featureElementId = map.getLayersByName("My Layer")[0].getFeaturesByAttribute("name","My Feature Name")[0].geometry.components[0].id;
Note that since this only grabs the element's id, you'll still need to use an appropriate method to grab a reference to the element itself. Something like either of the following:
var elementReference1 = document.getElementById(featureElementId);
var elementReference2 = jQuery('#'+featureElementId)[0];
Currently having a problem trying to get YUI Tooltips to display on top of a YUI Panel after it is shown that were previously created. The problem is is that the Panel cannot be registered to the overlay manager because it would require a TON of code to be changed and tested extending a hard deadline. The only way to get this to work is to setup the Tooltips after the Panel is shown. Problem there is the amount of code changes that would have to be done to attach another function call. My problem is that I was hoping that I could use the event handling to use "showEvent" but I cannot seem to get it to work (I apologize for word count):
var panel_obj = new YAHOO.widget.Panel('someID', {
width: "700px",
height: "500px",
close: true,
draggable: false,
modal: true,
constraintoviewport: true,
visible: false,
fixedcenter: true
});
panel_obj.render();
var tooltip_name = 'newTooltip1';
var element_id = 'htmlElementIDToBecomeTooltip';
function createTooltip() {
window[tooltip_name] = new YAHOO.widget.Tooltip(tooltip_name, {
context: element_id,
xyoffset: [15, -15],
zIndex: 999
});
}
function successfulScenario() {
panel_obj.show();
createTooltip();
}
function failedScenario1() {
YAHOO.util.Event.addListener(
'someID',
"showEvent",
createTooltip
);
}
function failedScenario2() {
createTooltip();
panel_obj.show();
}
The only way I have seem to get it working is by running something like successfulScenario(). I'm coming from a jQuery background so I'm still learning YUI. I would love to be able to just extend (subclass) YAHOO.widget.Panel's show() function to call createTooltip but I'm not that much of a guru or I would probably need to change a very large codebase to do it.
try using the "container" property for the tooltip config (so the container would be the panel's element):
function createTooltip() {
window[tooltip_name] = new YAHOO.widget.Tooltip(tooltip_name, {
container: panel_obj.element,
context: element_id,
xyoffset: [15, -15]
});
}
This is the quick solution, using the show event and/or extending the class would be nice but gotta run, if you still need help, I'll check back (also check the example that i made with your code http://jsfiddle.net/3GWaM/2/ ).
function createTooltip() {
var tooltipEl = document.createElement('DIV');
panel_obj.get('element').appendChild(tooltipEl);
window[tooltip_name] = new YAHOO.widget.Tooltip(tooltipEl, {
context: element_id,
xyoffset: [15, -15],
zIndex: 999
});
}
This will ensure the that the tool tip div is created inside the dialog box, instead of in the document body, ensuring it does not appear below the dialog box.
Also, if you want to extend the panel class just do the following
function MyPanel(el, config) {
MyPanel.superclass.constructor.apply(this, arguments);
this.createToolTip();
}
YAHOO.lang.extend(MyPanel, YAHOO.widget.Panel , {
createToolTip: function () {
// create tool tip here
this.on('show', this.showTooltip, this, true);
},
showToolTip: function () {this.toolTip.show();}
});
function getPanelIDFromElementID (element_id) {
var parent_panel = YAHOO.util.Dom.getAncestorByClassName(element_id, 'yui-panel');
var parent_id = null;
if (parent_panel) {
parent_id = parent_panel.id;
}
return parent_id;
}
function createTooltips() {
var tooltip_elements = YAHOO.util.Dom.getElementsByClassName('tooltip');
for (var i = 0; i < tooltip_elements.length; i++) {
var ele_id = tooltip_elements[i].getAttribute('id');
var name = ele_id.charAt(0).toLowerCase() + ele_id.slice(1);
var nameArray = name.split("_");
for (var x=1; x < nameArray.length; x++) {
nameArray[x] = nameArray[x].charAt(0).toUpperCase() + nameArray[x].slice(1);
}
var elementName = nameArray.join('');
window[elementName] = new YAHOO.widget.Tooltip(elementName, {
context: escape(ele_id),
xyoffset: [15, -15],
zIndex: 999,
container: getPanelIDFromElementID(ele_id)
});
}
}