Jquery Selector fails after setContent called on info window - javascript

I have a google maps info window that I open on a map, directly after which I want to bind event listeners to parts of the html in the info window. I do this with JQuery. The code goes something like:
myInfoWindow.open(map);
$('#someIdInInfoWindow').bind({
click: function() { foo1(); },
mouseenter: function() { foo2(); }
});
Unfortunately, I think Google makes most of their libraries asynchronous, so the content of the info window is not actually set until after the JQuery selector has been called.
How can I fix this so the event handlers are properly set? Thanks!

You must wait for the domready-event of the infoWindow before you add the listeners, at this time the HTML-element representing the infowindow has been attached to the document and it's contents are accessible :
google.maps.event.addListener(myInfoWindow,'domready',function(){
$('#someIdInInfoWindow').bind({
click: function() { foo1(); },
mouseenter: function() { foo2(); }
});
});
This applies when the content of the infoWindow is set via a string, when you assign it via a node you may also add the listeners directly to the particular element when you create it:
var content = $('<div/>')
.append($('<div/>',{id:'someIdInInfoWindow'})
.text('some text')
.click(foo1)
.mouseenter(foo2));
myInfoWindow.setContent(content[0]);
myInfoWindow.open(map);

Related

Components inserted into page using jquery's .load() function don't have a handle to javascript [duplicate]

On this page I have a jQuery popup window and thumbnail resizable images. If I mouse over on the thumbnails, the images are resizing perfectly. Also, when I click on the big yellow TV button "QuickBook TV" in the footer, the popup appears perfectly as I want it to.
However, when I click on the "Next" or "Prev" buttons, AJAX is used to load the new content and my jQuery no longer functions for the popup or thumbnail images. I have searched a number of forums looking for information on this issue, but due to having limited knowledge of jQuery I've been unable to understand what I need to do.
Following is the popup jQuery
$(document).ready(function() {
$(".iframe").colorbox({ iframe: true, width: "1000px", height: "500px" });
$(".inline").colorbox({ inline: true, width: "50%" });
$(".callbacks").colorbox({
onOpen: function() { alert('onOpen: colorbox is about to open'); },
onLoad: function() { alert('onLoad: colorbox has started to load the targeted content'); },
onComplete: function() { alert('onComplete: colorbox has displayed the loaded content'); },
onCleanup: function() { alert('onCleanup: colorbox has begun the close process'); },
onClosed: function() { alert('onClosed: colorbox has completely closed'); }
});
//Example of preserving a JavaScript event for inline calls.
$("#click").click(function() {
$('#click').css({ "background-color": "#f00", "color": "#fff", "cursor": "inherit" }).text("Open this window again and this message will still be here.");
return false;
});
});
And this is the thumbnails jQuery
$(function() {
var xwidth = ($('.image-popout img').width())/1;
var xheight = ($('.image-popout img').height())/1;
$('.image-popout img').css(
{'width': xwidth, 'height': xheight}
); //By default set the width and height of the image.
$('.image-popout img').parent().css(
{'width': xwidth, 'height': xheight}
);
$('.image-popout img').hover(
function() {
$(this).stop().animate( {
width : xwidth * 3,
height : xheight * 3,
margin : -(xwidth/3)
}, 200
); //END FUNCTION
$(this).addClass('image-popout-shadow');
}, //END HOVER IN
function() {
$(this).stop().animate( {
width : xwidth,
height : xheight,
margin : 0
}, 200, function() {
$(this).removeClass('image-popout-shadow');
}); //END FUNCTION
}
);
});
jQuery selectors select matching elements that exist in the DOM when the code is executed, and don't dynamically update. When you call a function, such as .hover() to add event handler(s), it only adds them to those elements. When you do an AJAX call, and replace a section of your page, you're removing those elements with the event handlers bound to them and replacing them with new elements. Even if those elements would now match that selector they don't get the event handler bound because the code to do that has already executed.
Event handlers
Specifically for event handlers (i.e. .click()) you can use event delegation to get around this. The basic principle is that you bind an event handler to a static (exists when the page loads, doesn't ever get replaced) element which will contain all of your dynamic (AJAX loaded) content. You can read more about event delegation in the jQuery documentation.
For your click event handler, the updated code would look like this:
$(document).on('click', "#click", function () {
$('#click').css({
"background-color": "#f00",
"color": "#fff",
"cursor": "inherit"
}).text("Open this window again and this message will still be here.");
return false;
});
That would bind an event handler to the entire document (so will never get removed until the page unloads), which will react to click events on an element with the id property of click. Ideally you'd use something closer to your dynamic elements in the DOM (perhaps a <div> on your page that is always there and contains all of your page content), since that will improve the efficiency a bit.
The issue comes when you need to handle .hover(), though. There's no actual hover event in JavaScript, jQuery just provides that function as a convenient shorthand for binding event handlers to the mouseenter and mouseleave events. You can, however, use event delegation:
$(document).on({
mouseenter: function () {
$(this).stop().animate({
width: xwidth * 3,
height: xheight * 3,
margin: -(xwidth / 3)
}, 200); //END FUNCTION
$(this).addClass('image-popout-shadow');
},
mouseleave: function () {
$(this).stop().animate({
width: xwidth,
height: xheight,
margin: 0
}, 200, function () {
$(this).removeClass('image-popout-shadow');
}); //END FUNCTION
}
}, '.image-popout img');
jQuery plugins
That covers the event handler bindings. However, that's not all you're doing. You also initialise a jQuery plugin (colorbox), and there's no way to delegate those to elements. You're going to have to simply call those lines again when you've loaded your AJAX content; the simplest way would be to move those into a separate named function that you can then call in both places (on page load and in your AJAX requests success callback):
function initialiseColorbox() {
$(".iframe").colorbox({
iframe: true,
width: "1000px",
height: "500px"
});
$(".inline").colorbox({
inline: true,
width: "50%"
});
$(".callbacks").colorbox({
onOpen: function () {
alert('onOpen: colorbox is about to open');
},
onLoad: function () {
alert('onLoad: colorbox has started to load the targeted content');
},
onComplete: function () {
alert('onComplete: colorbox has displayed the loaded content');
},
onCleanup: function () {
alert('onCleanup: colorbox has begun the close process');
},
onClosed: function () {
alert('onClosed: colorbox has completely closed');
}
});
}
Had the same problem before I was able to found the solution which worked for me.
So if anyone in future can give it a shot and let me know if it was right since all of the solutions I was able to find were a little more complicated than this.
So as said by Tamer Durgun, we will also place your code inside ajaxStop, so your code will be reinstated each time any event is completed by ajax.
$( document ).ajaxStop(function() {
//your code
}
Worked for me :)
// EXAMPLE FOR JQUERY AJAX COMPLETE FUNC.
$.ajax({
// get a form template first
url: "../FPFU/templates/yeni-workout-form.html",
type: "get",
success: function(data){
// insert this template into your container
$(".content").html(data);
},
error: function(){
alert_fail.removeClass("gizle");
alert_fail.addClass("goster");
alert_fail.html("Template getirilemedi.");
},
complete: function(){
// after all done you can manupulate here your new content
// tinymce yükleme
tinymce.init({
selector: '#workout-aciklama'
});
}
Your event handlers are being lost when you replace the content. When you set you hover events, jQuery is setting them on the events on the page currently. So when you replace them with ajax, the events are not associated with those elements because they are new.
To fix this you can either call the function that binds them again or you can instead set the event handler on the document as in this answer using $(document).on
That way the event is set on the document and any new elements will get the event called.
You Can User jQuery's delegate() method which Attach a handler to one or more events for all elements that match the selector, now or in the future, based on a specific set of root elements.In my case it's working as expected
this $(selector).click(function(e){}
become this after Using delegate() method
$( "body" ).delegate( "selector", "click", function(e) {}
Hope this will help ;)
You can use jQuery ajax's complete function after retrieving data form somewhere, it will see updated elements after ajax complete
This worked for me,
instead of:
$(document).ready(function(){
//code
});
I did:
$(document).on('mouseenter', function(){
//code
});
I'm late to the party but I would combine two of the answers. What worked for my specific needs was to incorporate the ajaxstop within the complete
complete: function () {
$( document ).ajaxStop(function() {
//now that all have been added to the dom, you can put in some code for your needs.
console.log($(".subareafilterActive").get().length)
})
}
Just an alternative.
$(window).on('load', _ => {
// some jQuery code ..
})
This binds any delegated handler to the window. It will fire once the window is fully loaded including all graphics/includes/hooks/requests not just the DOM.
$(document).ready(_ => ... preserves events to be fired after only the DOM is ready which does not apply on dynamically loaded content by AJAX. Either you can run a function or any event when a specific element is fully loaded by defining it as #Anthony Grist explained in his answer or bind your load event to the window as shown above.
https://api.jquery.com/load-event/
https://api.jquery.com/on/#on-events-selector-data-handler

Open Layers 3 Zoom map event handler

I need to handle a zoom event in Open Layers 3.
The following is my code:
map_object = new ol.Map({
target: 'map',
controls: controls_list,
interactions: interactions_list,
overlays: [overlay],
layers: [OSM_raster, WFS_layer],
view: view
});
map_object.on("Zoom", function() {
console.log('Zooming...');
});
This code runs with no errors and shows a map, but there is no output to the console, suggesting this function isn't firing.
I have also tried:
map_object.on("drag", function() {
console.log('Dragging...');
});
And this too does nothing.
Any help as to how to handle map control events in OL3 would be much appreciated (particularly zooming!). Note I have tried 'zoom' as well as 'Zoom' for the type field of the on method.
Just to add to this, you can check variations of events available with 'propertychange', from what I am seeing, there is no explicit .on ('zoom', ...) but rather you can access 'resolution' and other properties as mentioned in previous comments:
map.getView().on('propertychange', function(e) {
switch (e.key) {
case 'resolution':
console.log(e.oldValue);
break;
}
});
try with moveend event. (see https://openlayers.org/en/latest/apidoc/module-ol_MapEvent-MapEvent.html#event:moveend).
As mentioned by tonio, the way to listen on zoom change, which is called resolution change in openlayers terminology, is with
map.getView().on('change:resolution', (event) => {
console.log(event);
});
I find this is better (more succinct, less cruft) than listening on the general propertychange and verifying manually if the change concerns resolution.
This fires rapidly when using the mouse button so throttling it might be a good idea before launching any computation that waits for it to change.
Documentation for View
You can manage the moveend event...
We will need a global variable to alocate map’s view zoom level. I’ve
named it as currentZoomLevel.
There is available a moveend event. Let’s use it, and add a zoom level
check function..
In case of there’s a new zoom level, we trigger a zoomend event to
DOM’s document.
Finally we will need to add zoomend listener to the document element.
var = currentZoomLevel;
map.on('moveend', checknewzoom);
function checknewzoom(evt)
{
var newZoomLevel = map.getView().getZoom();
if (newZoomLevel != currentZoomLevel)
{
currentZoomLevel = newZoomLevel;
$(document).trigger("zoomend", zoomend_event);
}
}
$(document).on('zoomend', function () {
console.log("Zoom");
//Your code here
});
Source

Can't fire ouibounce.js modal on click

I'm trying to set up a page with a modal window which fires on either the mouse existing the window or on the click of a link.
Firing on exit is ok using Ouibounce:
http://carlsednaoui.github.io/ouibounce/
The documentation on the Ouibounce API suggests I should also be able to fire the modal via a click.
I have changed the link in the above example, giving it an id of #modal_button but I can't get it to fire the window:
// if you want to use the 'fire' or 'disable' fn,
// you need to save OuiBounce to an object
var _ouibounce = ouibounce(document.getElementById('ouibounce-modal'), {
aggressive: true,
timer: 0,
callback: function() { console.log('ouibounce fired!'); }
});
$('#modal_button').on('click', function() {
$('#ouibounce-modal').fire();
});
$('body').on('click', function() {
$('#ouibounce-modal').hide();
});
$('#ouibounce-modal .modal-footer').on('click', function() {
$('#ouibounce-modal').hide();
});
$('#ouibounce-modal .modal').on('click', function(e) {
e.stopPropagation();
});
I set up a jsfiddle here which does the same thing.
http://jsfiddle.net/fr7k3s6f/
(for some reason the 'hide" on the body event doesn't work in the jsfiddle)
You need to use your object _ouibounce to call the fire() function on:
_ouibounce.fire();
and not the jQuery object. And it needs to be a global object if you define it at jQuery.ready(), so no "var" before. But i think this is a bug in the current version (0.0.10).
I hope this helps.

Click link inside Leaflet Popup and do Javascript

I have a leaflet map up and running. It overlays a series of polygons (via GeoJSON) on the map and attaches popups to each polygon. Each of the popups display information about that polygon.
I'd like to have inside the popup a link that, when clicked, runs a javascript function that pulls further smaller polygons via AJAX and shows them.
I can't get the script to catch a click on the link via the normal jQuery/Javascript click events. Here's what I mean by normal (the following doesn't work):
$('a .smallPolygonLink').click(function(e){
console.log("One of the many Small Polygon Links was clicked");
});
The bindPopup part is as follows. It runs on each polygon when made and it pops up correctly on clicking on a polygon. It does show the link, just won't run the above code on click.
var popupContent = "Basic Information..." + '<a class="smallPolygonLink" href="#">Click here to see the smaller polygons</a>';
layer.bindPopup(popupContent);
Here's a JSFiddle illustrating the example, though in a far simpler form. http://jsfiddle.net/2XfVc/4/
The link element inside the popup is being dynamically generated from your markup each time the popup is opened. That means the link doesn't exist when you're trying to bind the handler to it.
The ideal approach here would be to use on to delegate event handling to the popup element or an ancestor of it. Unfortunately, the popup prevents event propagation, which is why delegating event handling to any static elements outside the popup won't work.
What you can do is preconstruct the link, attach the handler, and then pass it to the bindPopup method.
var link = $('TestLink').click(function() {
alert("test");
})[0];
marker.bindPopup(link);
Here is a demonstration: http://jsfiddle.net/2XfVc/7/
In general, to insert any sort of complex markup with multiple event handlers, use the folowing approach:
// Create an element to hold all your text and markup
var container = $('<div />');
// Delegate all event handling for the container itself and its contents to the container
container.on('click', '.smallPolygonLink', function() {
...
});
// Insert whatever you want into the container, using whichever approach you prefer
container.html("This is a link: <a href='#' class='smallPolygonLink'>Click me</a>.");
container.append($('<span class="bold">').text(" :)"))
// Insert the container into the popup
marker.bindPopup(container[0]);
Here is a demo: http://jsfiddle.net/8Lnt4/
See this Git issue for more on event propagation in leaflet popups.
While the Popup content wrapper prevents event propagation, events within the popup inner Markup propagate just fine. You can add events to popup elements when they are displayed on the map (and have become part of the DOM). Just watch for leaflet event popupopen.
var map = L.map('map').setView([51.505, 10], 7); //for example
//the .on() here is part of leaflet
map.on('popupopen', function() {
$('a .smallPolygonLink').click(function(e){
console.log("One of the many Small Polygon Links was clicked");
});
});
http://jsfiddle.net/tJGQ7/2/
This works like a charm for me. If your popup does not have a 'a .smallPolygonLink' the above code does nothing.
This code runs on every startup of a popup. However you don't have to worry that it attaches more than one handler to an element, since when the popup closes, the DOM nodes get thrown away.
There is a much more general way to do this. However, it involves eval(). Use at your own risk. But when AJAXloading partial pages that contain JS you run the same risks, so for your edification I present "executing JS inside your leaflet popups":
map.on('popupopen', function(){
var cont = document.getElementsByClassName('leaflet-popup-content')[0];
var lst = cont.getElementsByTagName('script');
for (var i=0; i<lst.length;i++) {
eval(lst[i].innerText)
}
});
demo: http://jsfiddle.net/tJGQ7/4/
Now you can write:
var popup_content = 'Testing the Link: TestLink<script> $(".speciallink").on("click", function(){alert("hello from inside the popup")});</script>';
marker.bindPopup(popup_content);
That's what I find on the mapbox offical website: Create a click event in a marker popup with Mapbox.js and jQuery. The comment explains why we say $('#map') instead of $('#mybutton').
var marker = L.marker([43.6475, -79.3838], {
icon: L.mapbox.marker.icon({
'marker-color': '#9c89cc'
})
})
.bindPopup('<button class="trigger">Say hi</button>')
.addTo(map);
//The HTML we put in bindPopup doesn't exist yet, so we can't just say
//$('#mybutton'). Instead, we listen for click events on the map element which will bubble up from the tooltip, once it's created and someone clicks on it.
$('#map').on('click', '.trigger', function() {
alert('Hello from Toronto!');});
I came across this problem, tried the solution above. But it didn't worked for me. Found the following pretty basic jquery solution.
// add your marker to the map
var my_marker = new L.marker([51.2323, 4.1231], {icon: my_icon});
var popup = L.popup().setContent('<a class="click" href="#">click</a>');
my_marker.addTo(map).bindPopup(popup);
// later on
jQuery("body").on('click','a.click', function(e){
e.preventDefault();
alert('clicked');
});
You can check inner properties of popup object, including _wrapper etc.
map.on('popupopen', _bindPopupClick);
map.on('popupclose', _unbindPopupClick);
var _bindPopupClick = function (e) {
if (e.popup) {
e.popup._wrapper.addEventListener('click', _bindPopupClickHandler);
}
};
var _unbindPopupClick = function (e) {
if (e.popup) {
e.popup._wrapper.removeEventListener('click', _bindPopupClickHandler);
}
}`
You can use jQuery to select the canvas element, but you'd have to use its own methods within the canvas. A decent start would be https://developer.mozilla.org/en/canvas_tutorial .
mapbox JavaScript library has an event:
bindPopup('<button class="trigger">Say hi</button>');
addTo(map);
$('#map').on('click', '.trigger', function() {
alert('Hello from Toronto!');
});
https://www.mapbox.com/mapbox.js/example/v1.0.0/clicks-in-popups/

add event handler an element inside a info window google maps v3

hi I'm trying to add to button inside an info window an event i tried like this
var infoWindow = new google.maps.InfoWindow({
content: '<div></br><span class="formatText">Ubicacion del Marcador: </span>' + event.latLng.toString()
+ '</br> <input type="button" id="btnPrueba" value="prueba"/></div>'
});
google.maps.event.addListener(marker, 'click', function () {
infoWindow.open(map, this);
google.maps.event.addDomListener(document.getElementById('btnPrueba'), 'click', removeMarker);
});
function removeMarker(){
alert('it works');
}
What am i doing wrong? or it's another way to do this?. Thanks
Edit
I'm also tried with jquery like this but although the event it's get by the function the alert doesn't show. When i do the debug with chrome it's works :(
google.maps.event.addListener(marker, 'click', function () {
infoWindow.open(map, this);
$("#btnPrueba").click(function () {
alert('funciona');
});
});
Try the "onclick" attribute for the button.
'<input type="button" id="btnPrueba" value="prueba" onclick="removeMarker()"/>'
Calling a global function like #DeeV's shown really does seem like the straightest shot at this (except for dirtying the global namespace a smidge which is looked down on these days). jQuery event delegation does work without any additional setTimeout gymnastics but feels like a heavier function call stack to accomplish the same goal. e.g.
$("#myMapContainer").on("click", "#btnPrueba", function() {alert "it works";});
However, sticking with the global approach, on top of #DeeV's answer, often the trick is getting some contextual values that you can act upon in that event handler:
one way is embedding parameter values to the function call in the
InfoWindow content string
along those lines, as you generate each Marker, push
it's reference to an array (also necessarily global) and thereby facilitate
Marker API calls later in the handler (e.g. setIcon(), etc.)
e.g. JS Pseudocode
var myMarkerArray = [];
function removeMarker(markerIndex) {
myMarkerArray[markerIndex].setMap(null);
}
for(int i=0; i<myLocationData.length; i++) {
var marker = new google.maps.Marker({...});
myMarkerArray.push(marker);
var popup = new google.maps.InfoWindow({ content: '<input type="button" id="btnPrueba" value="prueba" onclick="removeMarker('+i+')"/>' });
}
My workaround was to use a short timeout.
var infoWindow = ... (as in your question) ...
infoWindow.open(...);
setTimeout(function(){
$('#btnPrueba').on('click', function(e){
alert("It works at last!");
});
},50); //Wait for 0.05s
When I didn't use the setTimeout wrapper it did not work. Even though setContent() and open() had already been called. I assume this means the creation is done asynchronously.
NOTE: the "50" is a magic number, so potentially brittle.
NOTE: using the following also did not work for me, which I still find strange and cannot explain:
$('#btnPrueba').on('click', document, function(e){
alert("No luck...");
});

Categories

Resources