How to get contents on BindPopup - leaflet

I am trying to get marker's .bindPopup content on click event so I can save it to localStorage. But it is not working properly for each marker.
L.marker([76.920614, -60.117188])
.addTo(map)
.bindPopup('<div><span class="claimed">DATA 1</span></div>')
.on('click', groupClick);
L.marker([77.841848, -31.289063])
.addTo(map)
.bindPopup('<div><span class="claimed">DATA 2/span></div>')
.on('click', groupClick)
function groupClick(event) {
var a = document.querySelector('.claimed').innerHTML;
console.log(a);
}
it would work on first click but on the second click on different marker, it will take the data from the first marker that i clicked instead of the second marker. In this case i have to click somewhere else on the map or click the popup close button first before i can click on the next marker to properly get the data. is there any fix on this?

PROBLEM:
you are selecting in your function only the first appearance of the class (not the clicked ones child) at
var a = document.querySelector('.claimed').innerHTML;
SOLUTION 1 (NOT RECOMMENDED):
You should use the this keyword, and the getPopup() and getContent() methods instead, your function should look something like:
function groupClick(event) {
var a = this.getPopup().getContent();
console.log(a);
}
This way you'll get the escaped html, so a much better and proper way is...
SOLUTION 2 (RECOMMENDED): if you store the necessary data in the markers options (instead of storing and getting html from popup), like this:
L.marker([40, 12], {data: 1, datastring: 'first'})
.addTo(map)
.bindPopup('ONE')
.on('click', groupClick);
Then you can access this options in your function this way:
function groupClick(event) {
var a = this.options.data + ' ' + this.options.datastring;
alert(a);
}
A working fiddle again.
EDIT: I have found a workaround for the desired logic. But still, you need to link the marker and its popup content, because:
Popup content is not a node in the DOM, so you cannot access it before it is opened with a user click.
So in my solution i store a simple integer in the marker options (divId: int), which is the unique id of the marker. In the popup content, the radio inputs have the same id concatenated with the desired string (<input type="radio" id="Item-10-0" name="Item-10" value="0" checked="">).
L.marker([40, 32], {
divId: 10
})
.addTo(map)
.bindPopup('<div id="2div" class="popup-todo"><input type="radio" id="Item-10-0" name="Item-10" value="0" checked=""><label for="Item-10-0">Claimed</label><input type="radio" id="Item-10-1" name="Item-10" value="1"><label for="Item-10-1">Unclaimed</label></div>')
.on('click', groupClick);
If the user clicks, the new node is already accessible, so you can select it and use its id and value.
function groupClick(event) {
var a = document.querySelector('input[name=Item-'+this.options.divId+']');
document.querySelector('#demo').innerHTML = a.id + ' ' + a.value;
}
The fiddle.

Related

How can I write some javascript to click this "continue" button?

<span id="continue" class="a-button a-button-span12 a-button-primary"><span class="a-button-inner"><input id="continue" tabindex="5" class="a-button-input" type="submit" aria-labelledby="continue-announce"><span id="continue-announce" class="a-button-text" aria-hidden="true">
Continue
</span></span></span>
Above the the HTML from part of a page, which has a 'Continue' button that i'm trying to click, using my script.
So, writing in Javascript, i'm trying to click this button. But nothing I have tried works.
My attempted answer is:
function() {
var goButton = document.getElementById("continue");
goButton.click();},
Why doesn't it work? Help me, please !
You have set the ID of both the span and the input field to "continue". ID's should be unique for a single element. When I enter your code in the browser's console it returns the following:
> var goButton = document.getElementById("continue");
< undefined
> goButton.valueOf()
< <span id="continue" class="a-button a-button-span12 a-button-primary">
You can see the span is the element being selected instead of the input submit button. You should rename either of the 2 elements so both have a unique ID and use that in your script.
Edit: OP mentioned the HTML can not be changed so instead of fixing the use of a not-unique ID this Javascript can be used:
function() {
var continueSpan = document.getElementById("continue");
var goButton = continueSpan.firstElementChild.firstElementChild;
goButton.click();}

Mapbox GL Popup .setDOMContent example

I'm trying to create a customized button to appear on a pop up which generates a dynamic link (a URL). I don't seem to be able to do this via the .setHTML because of the timing, can't bind a button to a function at runtime. So I thought I'd try the newish .setDOMContent
There's zero information online as to how this feature works. I'm wondering if anyone has an example of this where a button is added to the popup that can run a function and send data.
Here's my very poor attempt at setting this up.
This function creates the popup
function GameObjectPopup(myObject) {
var features = map.queryRenderedFeatures(myObject.point, {
layers: ['seed']
});
if (!features.length) {
return;
}
var feature = features[0];
// Populate the popup and set its coordinates
// based on the feature found.
var popup = new mapboxgl.Popup()
.setLngLat(feature.geometry.coordinates)
.setHTML(ClickedGameObject(feature))
.setDOMContent(ClickedGameObject2(feature))
.addTo(map);
};
This function adds the html via the .setHTML
function ClickedGameObject(feature){
console.log("clicked on button");
var html = '';
html += "<div id='mapboxgl-popup'>";
html += "<h2>" + feature.properties.title + "</h2>";
html += "<p>" + feature.properties.description + "</p>";
html += "<button class='content' id='btn-collectobj' value='Collect'>";
html += "</div>";
return html;
}
This function wants to add the DOM content via the .setDOMContent
function ClickedGameObject2(feature){
document.getElementById('btn-collectobj').addEventListener('click', function()
{
console.log("clicked a button");
AddGameObjectToInventory(feature.geometry.coordinates);
});
}
I'm trying to pipe the variable from features.geometry.coordinates into the function AddGameObjectToInventory()
the error I'm getting when clicking on an object (so as popup is being generated)
Uncaught TypeError: Cannot read property 'addEventListener' of null
Popup#setHTML takes a string that represents some HTML content:
var str = "<h1>Hello, World!</h1>"
popup.setHTML(str);
while Popup#setDOMContent takes actual HTML nodes. i.e:
var h1 = document.createElement('h1');
h1.innerHTML="Hello, World";
popup.setDOMContent(h1);
both of those code snippets would result in identical Popup HTML contents. You wouldn't want to use both methods on a single popup because they are two different ways to do the same thing.
The problem in the code you shared is that you're trying to use the setDOMContent to add an event listener to your button, but you don't need to access the Popup object to add the event listener once the popup DOM content has been added to the map. Here is a working version of what I think you're trying to do: https://jsfiddle.net/h4j554sk/

Mouse over popup on leaflet.js marker

How can I add a mouse over pop up on leaflet.js marker . the pop up data will be dynamic.
I have a service which returns a lat & lon positions which will mark on a map.
I would require a popup on mouse over a marker . the event should send the lat and long position for ex to : http://api.openweathermap.org/data/2.5/weather?lat=40&lon=-100
the data from service should be in popup content.
I have tried but cant build the pop up content dynamically each marker
Please do the needful.
below is the code i have used for markers statesdata is array which stores the lat and longitude values
for ( var i=0; i < totalLength1; i++ ) {
var LamMarker = new L.marker([statesData1[i].KK, statesData1[i].LL]).on('contextmenu',function(e) {
onClick(this, i);
}).on('click',function(e) {
onClick1(this, i)
});
marker_a1.push(LamMarker);
map.addLayer(marker_a1[i]);
on click we call click1 function on context of marker we call click function
How can i add a pop on mouse over passing lat and long from the above code?
Attaching a popup to a marker is fairly easy. It is done by calling the bindPopup method of your L.Marker instance. Per default a popup opens on the click event of the L.Marker instance and closes on the click event of your L.Map instance. Now if you want to do something when a popup opens you can listen to the popupopen event of your L.Map instance.
When you want fetch external data in the background on the popupopen event that is usually done via XHR/AJAX. You can write your own logic or use something like jQuery's XHR/AJAX methods like $.getJSON. Once you receive response data you can then update your popup's content.
In code with comments to explain further:
// A new marker
var marker = new L.Marker([40.7127, -74.0059]).addTo(map);
// Bind popup with content
marker.bindPopup('No data yet, please wait...');
// Listen for the popupopen event on the map
map.on('popupopen', function(event){
// Grab the latitude and longitude from the popup
var ll = event.popup.getLatLng();
// Create url to use for getting the data
var url = 'http://api.openweathermap.org/data/2.5/weather?lat='+ll.lat+'&lon='+ll.lng;
// Fetch the data with the created url
$.getJSON(url, function(response){
// Use response data to update the popup's content
event.popup.setContent('Temperature: ' + response.main.temp);
});
});
// Listen for the popupclose event on the map
map.on('popupclose', function(event){
// Restore previous content
event.popup.setContent('No data yet, please wait...');
});
Here's a working example on Plunker: http://plnkr.co/edit/oq7RO5?p=preview
After comments:
If you want to open the popup on hover instead of click you can add this:
marker.on('mouseover', function(event){
marker.openPopup();
});
If you want to close the popup when you stop hovering instead of map click add this:
marker.on('mouseout', function(event){
marker.closePopup();
});
Here's an updated example: http://plnkr.co/edit/wlPV4F?p=preview
I got fed up with fighting with leaflet's built in functionality. The first thing I did was use the alt option to assign a key to the marker:
var myLocation = myMap.addLayer(L.marker(lat,lng],{icon: icon,alt: myKey}))
The next thing was assign an id using this alt and a title via jQuery (why you can't do that by default irritates me):
$('img[alt='+myKey+']').attr('id','marker_'+myKey).attr('title',sRolloverContent)
Then, I used jQuery tooltip (html will only render this way):
$('#marker_'+myKey).tooltip({
content: sRolloverContent
})
Also, by using the jQuery tooltip instead of the click-only bindPopup, I am able to fire the tooltip from my list, where the row has a matching key id:
$('.search-result-list').live('mouseover',function(){
$('#marker_'+$(this).attr('id')).tooltip('open')
})
$('.search-result-list').live('mouseout',function(){
$('#marker_'+$(this).attr('id')).tooltip('close')
})
By adding the id, I can easily use jQuery to do whatever I want, like highlight a list of locations when the marker is hovered:
$('#marker_'+iFireRescue_id).on('mouseover',function(){
('tr#'+getIndex($(this).attr('id'))).removeClass('alt').removeClass('alt-not').addClass('highlight')
})
$('#marker_'+myKey).on('mouseout',function(){
$('tr#'+getIndex($(this).attr('id'))).removeClass('highlight')
$('#search-results-table tbody tr:odd').addClass('alt')
$('#search-results-table tbody tr:even').addClass('alt-not')
})

Angular -- data-binding for a form that grows

I'm trying to create a form with data-binding that the user can add items to; they can click a button to add another text field (in this example it's the "plus" button). Here's a screenshot:
I've got things working now so more list items appear when the user clicks the button, but I can't find a clean and simple solution for how to let each form-element bind to a separate instruction in the model (theoretically in some sort of array in $scope.form). So right now, every instruction text area always contains the same text (as expected, which is the problem).
Here's my view code (in jade, but should be readable):
ol
li( ng-repeat='instruction in form.instructions' )
input( name='instruction[]' type='text' ng-model='form.instructions.text' )
| <br>
input( type='button' value='+' ng-click='addInstr()' )
Here's my controller code.
formControllers.controller('new-instruction-set-ctrl', function ($scope, $http) {
$scope.form = $scope.form || {};
$scope.form.instructions = [{}];
$scope.addInstr = function() {
$scope.form.instructions.push({});
};
});
This finally worked for me. See live demo in Plunker.
View:
li( ng-repeat='instruction in form.instructions' )
input( name='instruction[]' type='text' ng-model='form.instructions[$index].text')
I didn't have to change my controller at all.
$index is automatically provided by ng-repeat.

knockout.js - help dealing with UI state changes when polling for updates

I'm having a problem losing UI state changes after my observables change and was hoping for some suggestions.
First off, I'm polling my server for updates. Those messages are in my view model and the <ul> renders perfectly:
When my user clicks the "reply" or "assign to" buttons, I'm displaying a little form to perform those actions:
My problem at this point was that when my next polling call returned, the list re-binds and I lose the state of where the form should be open at. I went through adding view model properties for "currentQuestionID" so I could use a visible: binding and redisplay the form after binding.
Once that was complete, the form displays properly on the "current item" after rebinding but the form values are lost. That is to say, it rebinds, rebuilds the form elements, shows them, but any user input disappears (which of course makes sense since the HTML was just regenerated).
I attempted to follow the same pattern (using a value: binding to set the value and an event: {change: responseChanged} binding to update an observable with the values). The HTML fragment looks like this:
<form action="#" class="tb-reply-form" data-bind="visible: $root.showMenu($data, 'reply')">
<textarea id="tb-response" data-bind="value: $root.currentResponse, event: {keyup: $root.responseChanged}"></textarea>
<input type="button" id="tb-submitResponse" data-bind="click: $root.submitResponse, clickBubble: false" value="Send" />
</form>
<form action="#" class="tb-assign-form" data-bind="visible: $root.showMenu($data, 'assign')">
<select id="tb-assign" class="tb-assign" data-bind="value: $root.currentAssignee, options: $root.mediators, optionsText: 'full_name', optionsValue: 'access_token', optionsCaption: 'Select one...', event: {change: $root.assigneeChanged}">
</select>
<input type="button" id="tb-submitAssignment" data-bind="click: $root.submitAssignment, clickBubble: false" value="Assign"/>
</form>
Now, I end up with what seems like an infinite loop where setting the value causes change to happen, which in turn causes value... etc.
I thought "screw it" just move it out of the foreach... By moving the form outside of each <li> in the foreach: binding and doing a little DOM manipulation to move the form into the "current item", I figured I wouldn't lose user inputs.
replyForm.appendTo(theContainer).show();
It works up until the first poll return & rebind. Since the HTML is regenerated for the <ul>, the DOM no longer has my form and my attempt to grab it and do the .appendTo(container) does nothing. I suppose here, I might be able to copy the element into the active item instead of moving it?
So, this all seems like I'm missing something basic because someone has to have put a form into a foreach loop in knockout!
Does anybody have a strategy for maintaining form state inside a bound item in knockout?
Or, possibly, is there a way to make knockout NOT bind anything that's already bound and only generate "new" elements.
Finally, should I just scrap knockout for this and manually generate for "new items" myself when each polling call returns.
Just one last bit of info; if I set my polling interval to something like 30 seconds, all the bits "work" in that it submits, saves, rebinds, etc. I just need the form and it's contents to live through the rebinding.
Thanks a ton for any help!
Well, I figured it out on my own. And it's embarrassing.
Here is a partial bit of my VM code:
function TalkbackViewModel( id ) {
var self = this;
talkback.state.currentTalkbackId = "";
talkback.state.currentAction = "";
talkback.state.currentResponse = "";
talkback.state.currentAssignee = "";
self.talkbackQueue = ko.observableArray([]);
self.completeQueue = ko.observableArray([]);
self.mediators = ko.observableArray([]);
self.currentTalkbackId = ko.observable(talkback.state.currentTalkbackId);
self.currentAction = ko.observable(talkback.state.currentAction);
self.currentResponse = ko.observable(talkback.state.currentResponse);
self.currentAssignee = ko.observable(talkback.state.currentAssignee);
self.showActionForm = function(data, action) {
return ko.computed(function() {
var sameAction = (self.currentAction() == action);
var sameItem = (self.currentTalkbackId() == data.talkback_id());
return (sameAction && sameItem);
}, this);
};
self.replyToggle = function(model, event) {
// we're switching from one item to another. clear input values.
if (self.currentTalkbackId() != model.talkback_id() || self.currentAction() != "reply") {
self.currentResponse("");
self.currentAssignee("");
self.currentTalkbackId(model.talkback_id());
}
My first mistake was trying to treat the textarea & dropdown the same. I noticed the dropdown was saving value & reloading but stupidly tried to keep the code the same as the textarea and caused my own issue.
So...
First off, I went back to the using the $root view model properties for currentAssignee and currentResponse to store the values off and rebind using value: bindings on those controls.
Next, I needed to remove the event handlers:
event: { change: xxxChanged }
because they don't make sense (two way binding!!!!). The drop down value changes and updates automatically by using the value: binding.
The textarea ONLY updated on blur, causing me to think I needed onkeyup,onkeydown, etc. I got rid of those handlers because they were 1) wrong, 2) screwing up the value: binding creating an infinite loop.
I only needed this on the textarea to get up-to-date value updates to my viewmodel property:
valueUpdate: 'input'
At this point everything saves off & rebinds and I didn't lose my values but my caret position was incorrect in the textarea. I added a little code to handle that:
var item = element.find(".tb-assign");
var oldValue = item.val();
item.val('');
item.focus().val(oldValue);
Some browsers behave OK if you just do item.focus().val(item.val()); but i needed to actually cause the value to "change" in my case to get the caret at the end so I saved the value, cleared it, then restored it. I did this in the event handler for when the event data is returned to the browser:
$(window).on("talkback.retrieved", function(event, talkback_queue, complete_queue) {
var open_mappings = ko.mapping.fromJS(talkback_queue);
self.talkbackQueue(open_mappings);
if (talkback_queue) self.queueLength(talkback_queue.length);
var completed_mappings = ko.mapping.fromJS(complete_queue);
self.completeQueue(completed_mappings);
if (self.currentTalkbackId()) {
var element = $("li[talkbackId='" + self.currentTalkbackId() + "']");
if (talkback.state.currentAction == "assign") {
var item = element.find(".tb-assign");
var oldValue = item.val();
item.val('');
item.focus().val(oldValue);
} else {
var item = element.find(".tb-response");
var oldValue = item.val();
item.val('');
item.focus().val(oldValue);
}
}
}
);
So, my final issue is that if I used my observables in my method "clearing" the values when a new "current item" is selected (replyToggle & assignToggle), they don't seem to work.
self.currentResponse("");
self.currentAssignee("");
I cannot get the values to clear. I had to do some hack-fu and added the line below that to just work around it for now:
$(".tb-assign").val("");