4

I’m using the esri/InfoTemplate in the Javascript API to identify features. However, it gets anchored to the mouse click and cannot be moved around.

Based on this answer I used dojo/dnd/Moveable to make it moveable.

However, now I cannot highlight or copy the attributes inside of it. It’s like Moveable turns it into a graphic or something.

Is there a way to make the InfoTemplate floating but still be able to highlight and copy the attributes?

Thanks in advance for your help.

(The other option I’m considering is using the jQuery Dialog Widget, but I cannot get it to highlight the geometry for multiple features. Any suggestions there would be great too. Whichever is easier.)

  • Sounds like you're looking for a modal box rather than an infotemplate? if you're looking to identify features on the map, please try using esri/tasks/IdentifyTask – janechii Aug 27 '14 at 15:08
  • I don't want a modal box. I was wondering if there was a way to get the esri/InfoTemplate to not anchor on the map so users can drag it out of the way. It may not be possible so something else like jQuery Dialog may be needed. But I don't know how to get the dialog to show geometry for each feature identified. – kesselrun Sep 16 '14 at 18:50
3

I really appreciate all the suggestions, but I never got the esri/dijit/Popup to be moveable and still be able to highlight and copy the text inside. Instead I used a jQuery Dialog, which I figured would be the case but it just took a while to get all the experience needed to make it work the way I wanted it to.

My boss and I both worked on it and we still don't fully understand everything happening. But it works and that's all that matters to us.

Here’s what it looks like with jQuery theme “smoothness”.

enter image description here

Here is the basic sum up:

Since the inner content of a jQuery Dialog needs to be a div, I had to make an array of div elements with the identified attributes as the innerHTML.

  • When the map is clicked, the code cycles through the visible features. (I can’t remember where I got that from but it looks a lot like this http://jsfiddle.net/blordcastillo/mULcz/)

  • As it’s cycling through, a div is created for the attributes of each feature and the div is given a custom geometry attribute which is set to the geometry of the feature.

  • “Previous” and “next” buttons cycle through the array of divs and fires a function to draw the geometry on the map.

  • Using the geometry attribute of each div, a "Zoom to" button is added for zooming to the feature.

Basically, it acts the same as the Popup and InfoTemplate from the esri api, but now it is moveable around the screen and I can still interact with the text (highlight and copy).

Below is a copy of my module (cut down for size).

Feel free to either use it or “fork” it and let me know how to make it better. Hopefully it helps others.

Thanks again to everybody for all the suggestions!

define(["dojo/_base/declare", "dojo/dom", "dojo/_base/lang", "dojo/_base/array", "dojo/_base/Deferred", "dojo/DeferredList",
    "dojo/_base/Color", "dijit/registry",
    //esri
    "esri/tasks/IdentifyParameters", "esri/tasks/IdentifyTask", "esri/geometry/Extent",
    "esri/geometry/Point", "esri/graphic", "esri/SpatialReference", "esri/tasks/ProjectParameters",
    "esri/symbols/PictureMarkerSymbol", "esri/symbols/SimpleLineSymbol", "esri/symbols/SimpleFillSymbol"
], function (declare, dom, lang, array, Deferred, DeferredList, Color, registry,
    IdentifyParameters, IdentifyTask, Extent, Point, Graphic, SpatialReference, ProjectParameters, PictureMarkerSymbol, SimpleLineSymbol, SimpleFillSymbol) {

var $dialogDiv = $('#dialogDiv');
var featureArray = [];
var identifyParams = new IdentifyParameters();
identifyParams.tolerance = 5;
identifyParams.returnGeometry = true;
identifyParams.layerOption = IdentifyParameters.LAYER_OPTION_VISIBLE;

//point, line and polygon symbology for the DrawGeometry() function.
var ptSymbol = new PictureMarkerSymbol({
        "angle" : 0,
        "xoffset" : 0,
        "yoffset" : 0,
        "type" : "esriPMS",
        "url" : "http://" + server + "/apps/references/images/PointID32.png",
        "contentType" : "image/png",
        "width" : 24,
        "height" : 24
    });
var lineSymbol = new SimpleFillSymbol(SimpleFillSymbol.STYLE_SOLID, new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([0, 255, 255]), 3), null);
var fillSymbol = new SimpleFillSymbol(SimpleFillSymbol.STYLE_SOLID, new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([255, 0, 0]), 2), new Color([255, 255, 0, 0.25]));

return declare([], {

    executeIdentifyTask : function (evt) {
        var mapPoint = evt.mapPoint;
        myMap.setMapCursor("wait");
        idGL.clear();
        if (typeof dom.byId("divThing") !== "undefined" || dom.byId("divThing") !== null) {
            $("#divThing").remove();
        }
        var counter = 0;
        EmptyArray(featureArray);

        //I think this stuff came from here
        //http://jsfiddle.net/blordcastillo/mULcz/

        var layers = array.map(myMap.layerIds, function (layerId) {
                return myMap.getLayer(layerId);
            }); //Create an array of all layers in myMap
        layers = array.filter(layers, function (layer) {
                return layer.getImageUrl && layer.visible;
            }); //Only dynamic layers have the getImageUrl function. Filter so you only query visible dynamic layers
        var tasks = array.map(layers, function (layer) {
                return new IdentifyTask(layer.url);
            }); //myMap each visible dynamic layer to a new identify task, using the layer url. Set identifiable layerIds per service.
        var defTasks = array.map(tasks, function () {
                return new Deferred();
            }); //myMap each identify task to a new dojo.Deferred

        var dlTasks = new DeferredList(defTasks); //And use all of these Deferreds in a DeferredList
        dlTasks.then(showResults); //chain showResults onto your DeferredList/
        identifyParams.width = myMap.width;
        identifyParams.height = myMap.height;
        identifyParams.geometry = evt.mapPoint;
        identifyParams.mapExtent = myMap.extent;

        var i;
        for (i = 0; i < tasks.length; i++) { //Use 'for' instead of 'for...in' so you can sync tasks with defTasks
            try {
                //These are the services in the app.
                if (tasks[i].url === addressURL) {
                    identifyParams.layerIds = strucPts.visibleLayers; // [1,2,5,6];
                }
                if (tasks[i].url === transportationURL) {
                    identifyParams.layerIds = transPts.visibleLayers; //identifyParams.layerIds = [1, 2, 4, 5, 6];
                }
                if (tasks[i].url === cadastralURL) {
                    identifyParams.layerIds = cadasPoly.visibleLayers; //[0,1,3];
                }
                // Etc for all services
                tasks[i].execute(identifyParams, defTasks[i].callback, defTasks[i].errback); //Execute each task
            } catch (e) {
                console.log("Error caught");
                console.log(e);
                defTasks[i].errback(e); //If you get an error for any task, execute the errback
            }
        }
        function showResults(r) {

            myMap.setMapCursor("help");

            var results = [];
            r = array.filter(r, function () {
                    return r[0];
                }); //filter out any failed tasks

            for (i = 0; i < r.length; i++) {
                results = results.concat(r[i][1]);
            }

            results = array.map(results, function (result) {
                    var feature = result.feature;
                    feature.attributes.layerName = result.layerName;

                    ////ADDRESSING
                    if (result.layerName === "Address") {
                        var geom = feature.geometry;
                        var content = "";
                        if (lang.trim(feature.attributes.ADDRESS) !== "" && lang.trim(feature.attributes.ADDRESS) !== "Null") {
                            content += "<strong>Property Address:</strong> " + lang.trim(feature.attributes.ADDRESS) + "<br>";
                        }
                        if (lang.trim(feature.attributes.STREET) !== "" && lang.trim(feature.attributes.STREET) !== "Null") {
                            content += "<strong>Street:</strong> " + lang.trim(feature.attributes.STREET) + "<br>";
                        }
                        //Etc for all attributes
                        if (content === "" || content === "Null") {
                            content = "No information available at this time.";
                        }
                        CreateFeatureDiv(content, geom, result.layerName, featureArray);
                    }
                    ////CADASTRAL
                    if (result.layerName === "Cadastral") {
                        var geom = feature.geometry;
                        var content = "";
                        //Same if statements as above
                        CreateFeatureDiv(content, geom, result.layerName, featureArray);
                    }
                    ////TRANSPORTATION
                    if (result.layerName == 'Major Roads' || result.layerName == 'Local Roads') {
                        var geom = feature.geometry;
                        //Same if statements as above
                        CreateFeatureDiv(content, geom, result.layerName, featureArray);
                    }
                    //Etc, etc, etc for all layers in the map
                });

            if (typeof featureArray[counter] === "undefined" || featureArray[counter] === null) {
                $dialogDiv.dialog("close");
                return;
            }
            var divThing = document.createElement("div");
            divThing.id = "divThing";
            divThing.innerHTML = "<br>" + featureArray[counter].innerHTML;
            $dialogDiv.append(divThing);
            InsertZoomButton(divThing, featureArray[counter].geometry);
            DrawGeometry(featureArray[counter].geometry);
            EnableDisable(counter, featureArray);

            //// http://stackoverflow.com/questions/16549183/jquery-iterate-through-an-array-by-using-onclick
            dom.byId('prevFeature').onclick = function () {
                if (featureArray.length === 1) {
                    return;
                } else if (counter <= 0) {
                    counter = 0;
                    return;
                } else if (counter > 0) {
                    counter--;
                    FeatureClick(counter);
                } else {
                    return;
                }
            }
            dom.byId('nextFeature').onclick = function () {
                if (featureArray.length === 1) {
                    return;
                } else if (counter >= featureArray.length - 1) {
                    counter = featureArray.length - 1;
                    return;
                } else if (counter < featureArray.length - 1) {
                    counter++;
                    FeatureClick(counter);
                } else {
                    return;
                }
            }
            function FeatureClick(counter) {
                $("#divThing").remove();
                if (typeof featureArray[counter] === "undefined" || featureArray[counter] === null) {
                    return;
                }
                var feature = featureArray[counter];
                var divThing = document.createElement("div");
                divThing.id = "divThing";
                divThing.innerHTML = "<br>" + feature.innerHTML;
                var next = counter + 1;
                $dialogDiv.dialog('option', 'title', feature.title + " (" + next + " of " + featureArray.length + ")");
                $dialogDiv.append(divThing);
                DrawGeometry(feature.geometry);
                InsertZoomButton(divThing, feature.geometry);
                EnableDisable(counter, featureArray);
            }

            var place = counter + 1;
            $dialogDiv.dialog({
                title : featureArray[0].title + " (" + place + " of " + featureArray.length + ")",
                minWidth : 200,
                maxWidth : 800,
                minHeight : 300,
                maxHeight : 500,
                close : function () {
                    idGL.clear();
                    EmptyArray(featureArray);
                },
                show : 'fold',
                hide : 'fold',
                resizable : true
            });

            $dialogDiv.dialog("open");
        }
    }
});
function CreateFeatureDiv(htmlContent, geometry, featureName, array) {
    var div = document.createElement("div");
    div.innerHTML = htmlContent + "<br>";
    div["geometry"] = geometry;
    div["title"] = featureName;
    array.push(div);
}
function DrawGeometry(geometry) {
    switch (geometry.type) {
    case "point":
        var pt = new Point(geometry.x, geometry.y);
        var gr = new Graphic(pt, ptSymbol);
        idGL.clear();
        idGL.add(gr);
        break;
    case "polyline":
        var graphic = new Graphic(geometry, lineSymbol);
        idGL.clear();
        idGL.add(graphic);
        break;
    case "polygon":
        var graphic = new Graphic(geometry, fillSymbol);
        idGL.clear();
        idGL.add(graphic);
        break;
    default:
        return;
    }
}
function InsertZoomButton(div, geometry) {
    var btnZoom = document.createElement("button");
    var t = document.createTextNode("Zoom to");
    btnZoom.appendChild(t);
    div.appendChild(btnZoom);
    btnZoom.onclick = function () {
        switch (geometry.type) {
        case "point":
            var pt = new Point(geometry.x, geometry.y, myMap.spatialReference);
            myMap.setScale(2000);
            myMap.centerAt(pt);
            break;
        case "polyline":
            var findExtent = geometry._extent;
            var extent = new Extent({
                    "xmax" : findExtent.xmax,
                    "xmin" : findExtent.xmin,
                    "ymax" : findExtent.ymax,
                    "ymin" : findExtent.ymin,
                    "spatialReference" : {
                        "wkid" : 2243
                    }
                });
            myMap.setExtent(extent);
            break;
        case "polygon":
            var findExtent = geometry._extent;
            var extent = new Extent({
                    "xmax" : findExtent.xmax,
                    "xmin" : findExtent.xmin,
                    "ymax" : findExtent.ymax,
                    "ymin" : findExtent.ymin,
                    "spatialReference" : {
                        "wkid" : 2243
                    }
                });
            myMap.setExtent(extent);
            break;
        default:
            return;
        }
    };
}
function EmptyArray(array) {
    if (array.length > 0) {
        while (array.length > 0) {
            array.pop();
        }
    }
}
function EnableDisable(counter, array) {
    if (counter === 0 && counter === array.length - 1) {
        DisablePrev();
        DisableNext();
    } else if (counter === 0 && counter < array.length - 1) {
        DisablePrev();
        EnableNext();
    } else if (counter > 0 && counter < array.length - 1) {
        EnablePrev();
        EnableNext();
    } else if (counter > 0 && counter === array.length - 1) {
        EnablePrev();
        DisableNext();
    } else {
        console.log("Else returned");
    }
}
function DisablePrev() {
    registry.byId("prevFeature").set("iconClass", "prevFeature_gray");
    registry.byId("prevFeature").set("disabled", true);
}
function EnablePrev() {
    registry.byId("prevFeature").set("iconClass", "prevFeature");
    registry.byId("prevFeature").set("disabled", false);
}
function DisableNext() {
    registry.byId("nextFeature").set("iconClass", "nextFeature_gray");
    registry.byId("nextFeature").set("disabled", true);
}
function EnableNext() {
    registry.byId("nextFeature").set("iconClass", "nextFeature");
    registry.byId("nextFeature").set("disabled", false);
}

});
2

Try this:

Instead of making the whole popup box moveable, which is preventing you from interacting with text inside the box, make only the title bar (where the title and close button is) moveable.

To do this, you only have to specify the "handle" option of the Movable class:

new Moveable(popup, { handle: title_bar });

Implementation-wise, you'd probably do something like this

var popup = new Popup(...);
var title_bar = query('div.titlePane', popup.domNode)[0];
new Moveable(popup.domNode, { handle: title_bar });

query is a dojo CSS selector method from dojo/query.

This will make the whole popup moveable, but the title bar is the handle to move it, which will allow selection inside.

If that doesn't work because the domNode's content may not be available at the start, you can hook it in to popup's show event:

// depending on your esri jsapi, you may need to use dojo's on or connect instead
popup.on('show', function () { ... }); 

Good luck!

0

Try this:

 function makeMoveable(identifyDiv) {
                var title_bar = query('div.titlePane', identifyDiv.domNode)[0];

                var dnd = new dojo.dnd.Moveable(dojo.byId(identifyDiv), {skip: true, handle: title_bar});
            }
            makeMoveable("identifyDiv");

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.