Skip to content

How do I edit the popups bound to a polygon object

Currently, wherever I click on the map a popup appears with the country’s name. The country is determined by a geoJSON file that has the names and multi-polygon lnglat coordinates that sets the borders for each country to save me entering each one individually.

However, I want each popup bound to a leaflet polygon to display various amounts of information provided from different APIs such as REST Countries and Protected Planet. Nevertheless, I can’t seem to figure out how. I’d prefer to do this as the page loads, as if I did it each time the user clicked on a country that could cause an unnecessary amount of wait time to call the APIs every time a country is clicked.

Essentially I want the code to flow as follows: user clicks on country (multi-polygon object) -> popup bound to that feature/object has data related to that country called at the start of the page load (via name or ISO_a2 in PHP routine) OR when user clicks on the object a function is called to retrieve that information and populate the popup with that info.


//Populating <select>
$(function () {
    $.get('libs/json/countryBorders.geo.json').done(function (data) {
      data.features.forEach(function (feature) {
        $("<option>", {
          value: feature.properties.iso_a2,
          text: feature.properties.name
        }).appendTo("#countrySelect");
      });
    });
});
console.log("Select Function Loaded.");

//Mapping Borders + Popups
function addDataToMap(data, mymap) {
    var myStyle = {
        "color": "#36454f",
        "weight": 2,
        "opacity": 0,
        "fillOpacity": 0
    };
    L.geoJson(data, {
            style: myStyle,

            onEachFeature: function (feature, mymap) {
                var popupFeatureName = feature.properties.name;
                var popup = L.popup()
                    .setContent
                        (
                        "<div id='popupContent'>" 
                        + popupFeatureName + 
                        "</div>"
                        )
                    .openOn(mymap);
                var popupOptions =
                    {
                    'maxWidth': '500',
                    'className' : 'custom'
                    }
                mymap.bindPopup(popup, popupOptions)
        }
    }).addTo(mymap);
    
};
$.getJSON("libs/json/countryBorders.geo.json", function(data) { addDataToMap(data, mymap); });

Answer

For context, I am the original author of this question.

I managed to fix my issue with rigorous trial and error and formulate a working, albeit a convoluted, piece of code.

The largest issue was that Leaflet.js’ popup content would only accept a string. So even defining variables outside of the onEachFeature would generate errors regarding values being set to null and functions wouldn’t work or I’d get errors like “appendChild is not a function”.

With this complication, I had to try and create a workaround which resulted in 3 different solutions.


I’ll run down my 3 solutions in chronological order:

//Initial Attempt
/*"<table><tr><th class='display-4'>" + f.properties.name + "</th></tr>" + 
"<tr><td class='font-weight-bold'>Capital:</td><td>" + result["capital"] + "</td></tr>" +
"<tr><td class='font-weight-bold'>Population:</td><td>" + popStr + "</td></tr>" +
"<tr><td class='font-weight-bold'>Demonym:</td><td>" + result["demonym"] + "</td></tr>" +
"<tr><td class='font-weight-bold'>Languages:</td><td>" + result["languages"] + "</td></tr>" +
"<tr><td class='font-weight-bold'>Region:</td><td>" + result["region"] + "</td></tr>";*/

While the table approach worked, it wasn’t as easy to read and I decided to change my CSS styles to utilise Bootstrap, which was easier to designate each row as a div.

//Second Attempt
/*"<div class='container' id='appendButton'>" +
     "<div class='h3'>" + f.properties.name + "</div>" +
     "<div class='row'>" +
        "<div class='col'>Capital:</div>" +
        "<div class='col'>" + result["capital"] + "</div>" +
     "</div>" +
     "<div class='row'>" +
        "<div class='col'>Population:</div>" +
        "<div class='col'>" + popStr + "</div>" +
     "</div>" +
     "<div class='row'>" +
         "<div class='col'>Demonym:</div>" +
         "<div class='col'>" + result["demonym"] + "</div>" +
      "</div>" +
      "<div class='row'>" +
         "<div class='col'>Language:</div>" +
         "<div class='col'>" + result["languages"] + "</div>" +
      "</div>" +
      "<div class='row'>" +
         "<div class='col'>Region:</div>" +
         "<div class='col'>" + result["region"] + "</div>" +
      "</div><br/>" + "<button onclick='getProtectedPlanetAPI(f)' id='APIButton'>Protected Planet</button>" + //Button didn't work
      "</div>";*/

As you can see, while this worked and looked tidier and put together, the button didn’t work. This appeared to be a common or known issue with Leaflet when it came to trying to render a button on the map or popup.


Finally, I found a much better solution. The button is fully functional and while this consists of many more lines of code, you’ll find it’s probably the best way to get around the popup content issue. As well, generally, this is better practice in real-world applications.

function onEachFeature(f,l){
    var isoa3 = f.properties.iso_a3;
    var popupContent = "";
    
    $.ajax({
            url: "libs/php/getRESTCountryInfo.php",
            type: 'POST',
            dataType: 'json',
            data: {
                iso: isoa3
            },
            success: function(result) {
               // console.log(JSON.stringify(result));
               // console.log("JSON stringified");
            
                let pop = result["population"];
                let popStr = pop.toLocaleString("en-US");
                
                //Popup Content

                var popupCreateContainingDivElement = document.createElement("div");
                    popupCreateContainingDivElement.className = "container";

                var popupCreateMainDivElement = document.createElement("div");
                    popupCreateMainDivElement.className = "container";

                var popupCreateCountryNameHeading = document.createElement("div");
                    popupCreateCountryNameHeading.innerHTML = f.properties.name;
                    popupCreateCountryNameHeading.className = "h3";

                var popupCreateCapitalTR = document.createElement("div");
                    popupCreateCapitalTR.className = "row";
                    var popupCreateCapitalTD = document.createElement("div");
                        popupCreateCapitalTD.className = "col";
                        popupCreateCapitalTD.innerText = "Capital:";
                    var popupCreateCapitalResultTD = document.createElement("div");
                        popupCreateCapitalResultTD.className = "col";
                        popupCreateCapitalResultTD.innerText = result["capital"];
                popupCreateCapitalTR.appendChild(popupCreateCapitalTD);
                popupCreateCapitalTR.appendChild(popupCreateCapitalResultTD);

                var popupCreatePopulationTR = document.createElement("div");
                    popupCreatePopulationTR.className = "row";
                    var popupCreatePopulationTD = document.createElement("div");
                        popupCreatePopulationTD.className = "col";
                        popupCreatePopulationTD.innerText = "Population:";
                    var popupCreatePopulationResultTD = document.createElement("div");
                        popupCreatePopulationResultTD.className = "col";
                        popupCreatePopulationResultTD.innerText = popStr;
                popupCreatePopulationTR.appendChild(popupCreatePopulationTD);
                popupCreatePopulationTR.appendChild(popupCreatePopulationResultTD);

                var popupCreateDemonymTR = document.createElement("div");
                    popupCreateDemonymTR.className = "row";
                    var popupCreateDemonymTD = document.createElement("div");
                        popupCreateDemonymTD.className = "col";
                        popupCreateDemonymTD.innerText = "Demonym:"
                    var popupCreateDemonymResultTD = document.createElement("div");
                        popupCreateDemonymResultTD.className = "col";
                        popupCreateDemonymResultTD.innerText = result["demonym"];
                popupCreateDemonymTR.appendChild(popupCreateDemonymTD);
                popupCreateDemonymTR.appendChild(popupCreateDemonymResultTD);

                var popupCreateLanguagesTR = document.createElement("div");
                    popupCreateLanguagesTR.className = "row";
                    var popupCreateLanguagesTD = document.createElement("div");
                        popupCreateLanguagesTD.className = "col";
                        popupCreateLanguagesTD.innerText = "Languages:";
                    var popupCreateLanguagesResultTD = document.createElement("div");
                        popupCreateLanguagesResultTD.className = "col";
                        popupCreateLanguagesResultTD.innerText = result["languages"];
                popupCreateLanguagesTR.appendChild(popupCreateLanguagesTD);
                popupCreateLanguagesTR.appendChild(popupCreateLanguagesResultTD);

                var popupCreateRegionTR = document.createElement("div");
                    popupCreateRegionTR.className = "row";
                    var popupCreateRegionTD = document.createElement("div");
                        popupCreateRegionTD.className = "col";
                        popupCreateRegionTD.innerText = "Region:";
                    var popupCreateRegionResultTD = document.createElement("div");
                        popupCreateRegionResultTD.className = "col";
                        popupCreateRegionResultTD.innerText = result["region"];
                popupCreateRegionTR.appendChild(popupCreateRegionTD);
                popupCreateRegionTR.appendChild(popupCreateRegionResultTD);

                var popupCreateAdditionalInfoButton = document.createElement("button");
                    popupCreateAdditionalInfoButton.innerHTML = "Protected Planet";
                    popupCreateAdditionalInfoButton.onclick = getProtectedPlanetAPI;
                    

                popupCreateMainDivElement.appendChild(popupCreateCountryNameHeading);
                popupCreateMainDivElement.appendChild(popupCreateCapitalTR);
                popupCreateMainDivElement.appendChild(popupCreatePopulationTR);
                popupCreateMainDivElement.appendChild(popupCreateDemonymTR);
                popupCreateMainDivElement.appendChild(popupCreateLanguagesTR);
                popupCreateMainDivElement.appendChild(popupCreateRegionTR);
                popupCreateMainDivElement.appendChild(popupCreateAdditionalInfoButton);

                popupCreateContainingDivElement.appendChild(popupCreateMainDivElement);



                var expandedSection = document.createElement("div");
                    expandedSection.className = "container";
                    expandedSection.id = "expandedSection";
                
                popupCreateContainingDivElement.appendChild(expandedSection);


                if (result.status.name == "ok") {

                    popupContent = popupCreateContainingDivElement;
                    var popupOptions =
                        {
                        'minWidth': '300',
                        'className' : 'custom'
                        }
                    l.bindPopup(popupContent, popupOptions);
                }
            
            },
            error: function(jqXHR, textStatus, errorThrown) {
                console.log(jqXHR);
            }
    });