I'm using the datamaps library to display the US map using d3.js
I would like to only display a state when it is clicked. How can I do this using d3 or datamaps library?
var map = new Datamap({
element: document.getElementById('container'),
done: function(datamap) {
datamap.svg.selectAll('.datamaps-subunit').on('click', function(geography) {
alert(geography.properties.name);
});
}
});
I'm thinking about using the done callback (example shown above) to set the projection of the datamap. But it doesn't seem to be working at the moment.
Something like this:
datamap.svg.selectAll('.datamaps-subunit').on('click', function(geography) {
datamap.svg.selectAll('.datamaps-subunit')
.style('display', function(d){
return d.id === geography.id ? 'initial' : 'none'
});
datamap.svg.selectAll('.datamaps-arc')
.style('display', function(d){
//
});
datamap.svg.selectAll('.datamaps-bubble')
.style('display', function(d){
//
});
});
Related
Looking for the best way of implementing a custom hover with plotly.js along with react. The following is in the plotly.js docs https://plot.ly/javascript/hover-events/
var myPlot = document.getElementById('myDiv'),
hoverInfo = document.getElementById('hoverinfo'),
d3 = Plotly.d3,
N = 16,
x = d3.range(N),
y1 = d3.range(N).map( d3.random.normal() ),
y2 = d3.range(N).map( d3.random.normal() ),
data = [ { x:x, y:y1, type:'scatter', name:'Trial 1',
mode:'markers', marker:{size:16} },
{ x:x, y:y2, type:'scatter', name:'Trial 2',
mode:'markers', marker:{size:16} } ];
layout = {
hovermode:'closest',
title:'Hover on Points'
};
Plotly.plot('myDiv', data, layout);
myPlot.on('plotly_hover', function(data){
var infotext = data.points.map(function(d){
return (d.data.name+': x= '+d.x+', y= '+d.y.toPrecision(3));
});
hoverInfo.innerHTML = infotext.join('
');
})
.on('plotly_unhover', function(data){
hoverInfo.innerHTML = '';
});
But when using react I'm not sure how to get a reference to the #hoverInfo div. Any suggestions?
You can use ref
render() {
<div id='hoverInfo' ref={ (el) => this.hoverInfo = el }>
</div>
}
will give you a reference in your component to the hoverInfo div which can be used with a library like plotly.
I am facing the same problem and found the easiest way to customize hovers is using the hovertempleate property, if you only want to customized the text: https://plotly.com/javascript/hover-text-and-formatting/
Another option is playing with onHover and onUnhover props in the layout: https://github.com/plotly/react-plotly.js/ You can define an state hover which turns true when hovered and false when unhovered. With hover true you could make your component to appear with the information you need.
I have a google map in my view and in this map I have many marker and each marker should have a content which contains an a href for more details
This is my code for adding one marker which is used in a loop on all the events saved in the database :
setMarker(event){
let coords = event.location.split(",");
let location = new google.maps.LatLng(coords[0],coords[1]);
let marker = new google.maps.Marker({
map: this.map,
position: location
});
let content = "<h1>" + event.name + "</h1></br><a ng-click="+this.viewEventDetails(event)+"> More Details</a>";
let infoWindow = new google.maps.InfoWindow({
content: content
});
google.maps.event.addListener(marker, 'click', () => {
infoWindow.open(this.map, marker);
});
return marker;
}
and this is my viewEventDetails function :
viewEventDetails(event){
this.navCtrl.push(EventDetailsPage, event);
}
When I load the map view it loads the viewEventDetails on all events , what should I do please ?
Could you change "</h1></br><a ng-click="+this.viewEventDetails(event)+"> More Details</a>" to "</h1></br><a ng-click=\"viewEventDetails(event)\"> More Details</a>". I think this should work.
Btw I think ng-click should also be substitude with (click) since ionic 2 does the angular2 way and therefore does not accept ng-click.
I'm trying to implement Google maps in Angularjs using ui.Map (http://angular-ui.github.io/ui-map/)
I've followed the example pretty closely and the map loads, I can create a marker in the map center and the 'map-tilesloaded' event works fine.
My problem is adding a marker where the user clicks. The click function is receiving an empty $params parameter. In my controller:
$scope.newMapOptions = {
center : new google.maps.LatLng($scope.position.lat, $scope.position.lng),
zoom : 18,
mapTypeId : google.maps.MapTypeId.ROADMAP
};
$scope.getLocation = function() {
if (navigator.geolocation) {
return navigator.geolocation.getCurrentPosition(setPosition);
}
};
$scope.addMarker = function($event, $params) {
$scope.newTingMarker = new google.maps.Marker({
map : $scope.myNewTingMap,
position : $params[0].latLng
});
};
$scope.initMap = function() {
if (!$scope.mapLoaded)
$scope.getLocation();
$scope.mapLoaded = true;
};
function setPosition(pos) {
$scope.position = {
lat : pos.coords.latitude,
lng : pos.coords.longitude
};
$scope.meMarker = new google.maps.Marker({
map : $scope.myNewTingMap,
position : new google.maps.LatLng($scope.position.lat, $scope.position.lng)
});
$scope.myNewTingMap.setCenter(new google.maps.LatLng(pos.coords.latitude, pos.coords.longitude));
$scope.$apply();
}
The html:
<div ui-map-info-window="myInfoWindow">
<b>Current location</b>
</div>
<div ui-map-marker="meMarker" ></div>
<div ui-map-marker="newTingMarker" ui-event="{'map-click': 'openMarkerInfo(newTingMarker)'}"></div>
<section id="newTingMap" >
<div ui-map="myNewTingMap" ui-options="newMapOptions" class="map-canvas"
ui-event="{'map-tilesloaded': 'initMap()', 'map-click': 'addMarker($event, $params)' }"></div>
</section>
$scope.addMarker should receive $event and $params where $params[0] has the latlng object. At the moment is it an empty array: []
I'm using angular 1.1.5, but I've tried using the same as the ui.Map example with no effect.
I should also note that this is in a view but putting it outside the view in the main controller makes no difference.
If I try to follow the code running from the ui-map directive I can see that the latlng object does start off in the event:
ui-map.js:
angular.forEach(eventsStr.split(' '), function (eventName) {
//Prefix all googlemap events with 'map-', so eg 'click'
//for the googlemap doesn't interfere with a normal 'click' event
google.maps.event.addListener(googleObject, eventName, function (event) {
element.triggerHandler('map-' + eventName, event);
//We create an $apply if it isn't happening. we need better support for this
//We don't want to use timeout because tons of these events fire at once,
//and we only need one $apply
if (!scope.$$phase){ scope.$apply();}
});
});
element.triggerHandler('map-' + eventName, event); ... has the latlng object in 'event' but is seems to get lost after that
Not sure what your issue is, I took your code and created a fiddle that works fine(something you should have done).
I did a console log when you click that logs the $params.
The most important thing to note is your code crashes at first because you reference $scope.position.lat before setting it. I updated it to default to RVA.
,
You do need to handle the case a little more gracefully.
function MapCtrl($scope, watchArray) {
var center;
if ($scope.position) {
center = new google.maps.LatLng($scope.position.lat, $scope.position.lng);
}
else {
center = new google.maps.LatLng(37.5410, 77.4329); //if null use rva
}
$scope.newMapOptions = {
center: center,
zoom: 18,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
...
}
Console.log:
[Ps]
v 0: Ps
> la: Q
> latLng: O
> pixel: Q
> __proto__: Ps
length: 1
> __proto__: Array[0]
I'm working on a medium-complex app using backbone.js to handle wordpress data, and i can't figure out how to get the force working in a backbone layout.
basically, i'm trying to instantiate a force layout within a backbone boilerplate layout, like this:
myLayout = Backbone.Layout.extend({
initialize: function() {
var f = this; // i.e. the layout instance
f.force = d3.layout.force()
.nodes(myModels)
.on("tick", f.tick)
.gravity(0)
.friction(0.9)
.start();
console.log(f.force);
},
tick: function() {
// stuff to do when the force ticks
}
});
The problem is that the force is being defined with all blank functions, like gravity: function(x) { //lots of null things here }. i'm pretty sure it's a namespacing issue, but nothing i try works - i've tried doing $(window).force, var force, $this.force...
in my example tick is the only namespaced function, but i've tried doing that with all the others too (gravity, friction, etc.) to no avail (even though they should just be chaining onto the force object).
anyone have any ideas? i can't really post a .jsfiddle because the app is too complicated, so sorry in advance about that. The current version is up here
edit: here's how d3 can access the models successfully:
this works:
myLayout.nodes = myLayout.d3_wrapper.selectAll(".node")
.data(myModels)
.enter().append("g").attr("class", "node")
.attr("x",10)
.attr("y",10);
myLayout.nodes.append("clipPath")
.attr("id", function(d) { return d.get("slug"); })
as does this:
myLayout.nodes.append("clipPath")
.attr("id", function(d) { return d.attributes.slug });
edit: in the interest of clarity, here's the non-nicknamed code:
setforce: function() { // this gets called from the layout's initialize fn
console.log("setting force");
var f = this; // the layout
f.force = d3.layout.force()
.nodes(Cartofolio.elders.models) // Cartofolio is the module, elders is a Backbone Collection
.gravity(0)
.friction(0.9)
.start();
console.log(f.force);
}
I would try using toJSON() on your collection before passing it to d3:
myLayout = Backbone.Layout.extend({
initialize: function() {
var f = this; // i.e. the layout instance
f.force = d3.layout.force()
.nodes(myModels.toJSON())
.on("tick", f.tick)
.gravity(0)
.friction(0.9)
.start();
console.log(f.force);
},
tick: function() {
// stuff to do when the force ticks
}
});
I am working with Twitter Bootstrap and ran into something I could not fix when testing on iPad and iPhone. On mobile (at least those devices) you need to click to engage the tip or popover (as expected). The issue is that you can never close it once you do. I added a listener to close it if you click it again, but I find it hard to believe that the default behavior would not be to click to remove it. Is this a bug in Bootstrap popover and tooltip?? My code is below - it seems to work, but ONLY if you click the same item that created the tip or popover - not anywhere on the page (could not get that to work).
Code to fire:
$(function () {
//Remove the title bar (adjust the template)
$(".Example").popover({
offset: 10,
animate: false,
html: true,
placement: 'top',
template: '<div class="popover"><div class="arrow"></div><div class="popover-inner"><div class="popover-content"><p></p></div></div></div>'
//<h3 class="popover-title"></h3>
//Need to have this click check since the tooltip will not close on mobile
}).click(function(e) {
jQuery(document).one("click", function() {
$('.Example').popover('hide')
});
});
});
HTML:
<a href="javascript:void(0);" class="Example" rel="popover" data-content="This is the Data Content" data-original-title="This is the title (hidden in this example)">
Thanks in advance!
Dennis
I tried dozens of solutions posted to stackoverflow and other various corners of the web, and the following is the only one that worked for me!
Explanation
As noted here, you can a CSS-directive the element in order to make it touch-device-clickable. I can't tell you why that works or what's going on there, but that seems to be the case. So, I want to make the entire document aka body clickable on mobile devices, which will allow me to touch anywhere to dismiss the popover.
Popover JS
$(function () {
$('[data-toggle="popover"]').popover({ trigger: "hover"}})
});
Directions
1. Install Modernizr
I'm using rails, so I used the gem.
gem 'modernizr-rails'
2. Create a touch class with a css-directive
Add the following to your CSS:
.touch {
cursor: pointer
}
3. On touch devices only, add the touch class to the body
If you want other elements to be clickable, instead of the entire body, add the touch class to them.
if (Modernizr.touch) {
$( "body" ).addClass( "touch" );
}
That's it! Now, you can use your popover normally on desktop (even with hover-trigger) and it will be touch-dismissible on mobile.
I had the same problem with my IPad. But in browser it works fine. Solution for me was adding listeners for all possible element that i can hide tooltip:
$('*').bind('touchend', function(e){
if ($(e.target).attr('rel') !== 'tooltip' && ($('div.tooltip.in').length > 0)){
$('[rel=tooltip]').mouseleave();
e.stopPropagation();
} else {
$(e.target).mouseenter();
}
});
Yes, it's small overhead to send event for all tooltips, but you can't define which element tooltip is showing.
Main concept is that make popover manually on mobile device
$(document).ready(function() {
if ('ontouchstart' in window) {
$('[data-toggle="popover"]').popover({
'trigger': 'manual'
});
}
});
Refer following code snippet to get it works:
$('[data-toggle="popover"]').popover();
$('body').on('click', function (e) {
$('[data-toggle="popover"]').each(function () {
//the 'is' for buttons that trigger popups
//the 'has' for icons within a button that triggers a popup
if (!$(this).is(e.target) && $(this).has(e.target).length === 0 && $('.popover').has(e.target).length === 0) {
$(this).popover('hide');
}
});
});
This is the easiest way of detecting clicks on the body and close all the tooltips on the page.
You can check the live example here
Solution on this jsfiddle,
test on iOS (iPad and iPhone), Android and Windows.
$(document).ready(function(){
var toolOptions;
var toolOptions2;
var isOS = /iPad|iPhone|iPod/.test(navigator.platform);
var isAndroid = /(android)/i.test(navigator.userAgent);
///////////////////////////////////////// if OS
if (isOS){
toolOptions = {
animation: false,
placement:"bottom",
container:"body"
};
$('.customtooltip').tooltip(toolOptions);
$('.customtooltip').css( 'cursor', 'pointer' );
$('body').on("touchstart", function(e){
$(".customtooltip").each(function () {
// hide any open tooltips when the anywhere else in the body is clicked
if (!$(this).is(e.target) && $(this).has(e.target).length === 0 && $('.tooltip').has(e.target).length === 0) {
$(this).tooltip('hide');
}////end if
});
});
///////////////////////////////////////// if Android
} else if(isAndroid){
toolOptions = {
animation: false,
placement:"bottom",
container:"body"
};
toolOptions2 = {
animation: false,
placement:"left",
container:"body"
};
$('.c_tool1').tooltip(toolOptions);
$('.c_tool2').tooltip(toolOptions);
$('.c_tool3').tooltip(toolOptions2);
///////////////////////////////////////// if another system
} else {
toolOptions = {
animation: true,
placement:"bottom",
container:"body"
};
$('.customtooltip').tooltip(toolOptions);
}//end if system
document.getElementById("demo").innerHTML = "Sys: "+navigator.platform+" - isOS: "+isOS+" - isAndroid: "+isAndroid;
});
<h6>
first tooltip
Second tooltip
third tooltip
</h6>
<p id="demo"></p>
Bootstap-tooltip v3.3.7
Actual: tooltip on hover doesn't work with touch devices in our project
Solution: Subscribe to tooltip's show event and call mouseenter
$body = $('body');
$body.tooltip({selector: '.js-tooltip'});
// fix for touch device.
if (Modernizr.touch) { // to detect you can use https://modernizr.com
var hideTooltip = function(e) {
tooltipClicked = !!$(e.target).closest('.tooltip').length;
if (tooltipClicked) { return; }
$('.js-tooltip').tooltip('hide');
}
var emulateClickOnTooltip = function(e) {
tooltipsVisible = !!$('.tooltip.in').length;
if (tooltipsVisible) { return; }
$(e.target).mouseenter();
}
var onTooltipShow = function(e) {
tooltipClicked = !!$(e.target).closest('.tooltip').length;
if (tooltipClicked) { return; }
$body.on('touchend', hideTooltip);
}
var onTooltipHide = function() {
$body.off('touchend', hideTooltip);
}
$body
.on('touchend', '.js-tooltip', emulateClickOnTooltip)
.on('show.bs.tooltip', onTooltipShow)
.on('hide.bs.tooltip', onTooltipHide);
}