Edit in JSFiddle



  // ページの読み込みが終わったら、地図表示の処理を行う
  window.addEventListener("DOMContentLoaded", function () {
  	var posts = JSON.parse(document.getElementById("data").innerHTML);
      
    // マーカーとツールチップを、店舗情報毎に設定するための overlay
    var overlay = new OpenLayers.Layer.Vector('Overlay', {
      styleMap: new OpenLayers.StyleMap({
        externalGraphic: '${marker_image}',     // ■(I)-1
        graphicWidth: 21,
        graphicHeight: 25,
        graphicYOffset: -25,
        cursor: "pointer",
        title: '${tooltip}',    // ■(J)-1
      })
    });


    var icon_map = {
      2: "http://dev.openlayers.org/img/marker.png",
      3: "http://dev.openlayers.org/img/marker-blue.png",
      4: "http://dev.openlayers.org/img/marker-gold.png",
      5: "http://dev.openlayers.org/img/marker-green.png",
    };

    var epsg4326 = new OpenLayers.Projection("EPSG:4326");
    var epsg3857 = new OpenLayers.Projection("EPSG:3857");

    // マーカーの生成
    var features = [];
    for (var i in posts) {
      var post = posts[i];
      features.push(new OpenLayers.Feature.Vector(    // ■(H)-1
          new OpenLayers.Geometry.Point(post.lng, post.lat)
          .transform(epsg4326, epsg3857), {
            tooltip: post.title,                    // ■(J)-2
            marker_image: icon_map[ post.cat_id ],  // ■(I)-2
            post: post,
          }
      ));
    }

    overlay.addFeatures(features);  // ■(H)-2

    overlay.events.on({     // ■(L)-1
      'featureclick': function(evt) {
        toggle_popup(evt.feature);
      },
    });

    // マップの生成
    var map = new OpenLayers.Map("map");    // ■(A)、■(C)
    
    map.addLayers([
      new OpenLayers.Layer.OSM(),           // ■(A)、
      overlay                               // ■(H)-3
    ]);
    map.addControl(new OpenLayers.Control.ScaleLine({   // ■(E)
      bottomOutUnits: "",
      bottomInUnits: ""
    }));

    
    // マーカーのクリック状態を取得するために、SelectFeature なるものを作る
    var select = new OpenLayers.Control.SelectFeature(overlay);   // ■(L)-2
    map.addControl(select);
    select.activate();

    // マーカーが含まれる範囲の Bounds と、それに合わせたズーミング
    var bounds = new OpenLayers.Bounds();   // ■(F)-2
    for (var i in posts) {
      bounds.extend(new OpenLayers.LonLat(posts[i].lng, posts[i].lat));
    }
    bounds.transform(epsg4326, epsg3857);
    map.zoomToExtent(bounds);   // ■(F)-1、■(D)

    // ズームの最大値は 16 まで // ■(G)
    if (map.getZoom() > 16) {
      map.zoomTo(16);
    }


    // 吹き出しの「×」を押したときの処理
    function close_popup(evt) {
      toggle_popup(this.feature);
    }

    // マーカーをクリックしたときに吹き出しの表示を切り替える
    function toggle_popup(feature) {
      if (! feature.popup) {
        var post = feature.attributes.post;
        var content = '<div>' + post.name + ':' +
              '<a href="' + post.link + '">' + post.title +
              '</a></div>';

        var popup = new OpenLayers.Popup.FramedCloud(       // ■(K)
          "featurePopup",
          feature.geometry.getBounds().getCenterLonLat(),
          new OpenLayers.Size(100, 100),
          content,
          null, true, close_popup
        );
        popup.calculateRelativePosition = function() {      // ■(M)
          return "bl";
        };
        feature.popup = popup;
        popup.feature = feature;
        feature.layer.map.addPopup(popup);    // ■(L)-3
      } else {
        var popup = feature.popup;
        popup.feature = null;
        feature.layer.map.removePopup(feature.popup);       // ■(L)-4
        feature.popup.destroy();
        feature.popup = null;
      }
    }

  });
<div id="map" style="width: 99%; height: 650px;"></div>


<script id="data" type="text/json">
[
  {
    "lat": 35.701418,
    "lng": 139.74787,
    "name": "和食料理店",
    "link": "http://www.google.com/search?q=%e5%92%8c%e9%a3%9f%e5%89%b2%e7%83%b9zen",
    "title": "和食割烹ZEN",
    "cat_id": "2"
  },
  {
    "lat": 35.6950886,
    "lng": 139.74937320000004,
    "name": "犬猫病院",
    "link": "http://www.google.com/search?q=%e3%82%a2%e3%83%8b%e3%83%9e%e3%83%ab%e3%83%9b%e3%82%b9%e3%83%94%e3%82%bf%e3%83%ab",
    "title": "アニマルホスピタル",
    "cat_id": "5"
  },
  {
    "lat": 35.67694669999999,
    "lng": 139.7635034,
    "name": "カフェ",
    "link": "http://www.google.com/search?q=%e3%82%b9%e3%82%a4%ef%bc%8d%e3%83%84%e3%83%99%e3%83%aa%ef%bc%8d",
    "title": "スイ-ツベリ-",
    "cat_id": "2"
  },
  {
    "lat": 35.6964531,
    "lng": 139.77445769999997,
    "name": "服屋",
    "link": "http://www.google.com/search?q=%e3%83%a6%e3%83%8b%e3%82%b7%e3%83%ad",
    "title": "ユニシロ",
    "cat_id": "4"
  },
  {
    "lat": 35.6964946,
    "lng": 139.77230780000002,
    "name": "不動産屋",
    "link": "http://www.google.com/search?q=%e3%82%a8%e3%82%b9%e3%83%86%ef%bc%8d%e3%83%88%e6%9d%b1%e4%ba%ac",
    "title": "エステ-ト東京",
    "cat_id": "3"
  },
  {
    "lat": 35.6945504,
    "lng": 139.770258,
    "name": "たこ焼き屋",
    "link": "http://www.google.com/search?q=%e3%81%8f%e3%82%8b%e3%81%8f%e3%82%8b",
    "title": "くるくる",
    "cat_id": "2"
  },
  {
    "lat": 35.6963474,
    "lng": 139.77209949999997,
    "name": "ペット",
    "link": "http://www.google.com/search?q=%e3%83%9a%e3%83%83%e3%83%88%e3%82%b7%e3%83%a7%e3%83%83%e3%83%97%e3%82%8f%e3%82%93%e3%81%ab%e3%82%83%e3%82%93",
    "title": "ペットショップわんにゃん",
    "cat_id": "5"
  },
  {
    "lat": 35.6950947,
    "lng": 139.7685136,
    "name": "工務店",
    "link": "http://www.google.com/search?q=%e3%83%88%e3%83%b3%e3%82%ab%e3%83%81%e5%b7%a5%e5%8b%99%e5%ba%97",
    "title": "トンカチ工務店",
    "cat_id": "3"
  },
  {
    "lat": 35.6971917,
    "lng": 139.76922739999998,
    "name": "お好み焼き",
    "link": "http://www.google.com/search?q=%e3%81%98%e3%82%85%e3%81%86%e3%81%98%e3%82%85%e3%81%86",
    "title": "じゅうじゅう",
    "cat_id": "2"
  },
  {
    "lat": 35.6749887,
    "lng": 139.76436450000006,
    "name": "美容室",
    "link": "http://www.google.com/search?q=%e3%82%b5%e3%83%a9%e3%82%b5%e3%83%a9%e7%be%8e%e5%ae%b9%e5%ae%a4",
    "title": "サラサラキュ-ティクル",
    "cat_id": "4"
  },
  {
    "lat": 35.6817879,
    "lng": 139.76676139999995,
    "name": "パン屋",
    "link": "http://www.google.com/search?q=%e3%83%91%e3%83%b3%e5%b1%8b%e3%82%a4%ef%bc%8d%e3%82%b9%e3%83%88",
    "title": "イ-ストパン",
    "cat_id": "2"
  }
]
</script>
/* 地図領域と地図のサイズが合わないとき : Google Maps は API が設定する */
#map {
  position: relative;     /* ■(N) */
  overflow: hidden;
}
/* style.css の指定が影響して、吹き出しが正しく表示されない */
#map .olPopup img {
  max-width: none;
}