Заметки за март 2010 года

Получаем местоположение пользователя

Если ваши клиенты проживают в разных городах, и для каждого из них вы хотите предложить какие-то товары и услуги где-нибудь поблизости, то, скорее всего, нужно будет сделать селектор со списком городов или регионов. Но будет проще, если мы сами попытаемся определить местоположение пользователя. В этом нет никакого волшебства.

Сопоставляем IP-адрес пользователя его местоположению

У каждого компьютера в сети есть идентификатор — IP-адрес. Он назначается Интернет-провайдером, к которому подключен пользователь. А так как блоки адресов привязаны к конкретному городу, региону или стране, то этого достаточно, чтобы определить местоположение ваших пользователей.

Такую базу адресов можно хранить у себя на сервере, а можно воспользоваться сторонними решениями, например, Maxmind GeoIP database. Maxmind так же предоставляет JavaScript решение, которое можно использовать на своих сайтах:

<script type="text/javascript" src="http://j.maxmind.com/app/geoip.js"></script>
<script type="text/javascript">
$(function () {
    var lat = geoip_latitude();
    var lon = geoip_longitude();
    var city = geoip_city();
    var out = '<h3>Information from your IP</h3>'+
        '<ul>'+
        '<li>Latitude: ' + lat + '</li>'+
        '<li>Longitude: ' + lon + '</li>'+
        '<li>City: ' + city + '</li>'+
        '<li>Region: ' + geoip_region() + '</li>'+
        '<li>Region Name: ' + geoip_region_name() + '</li>'+
        '<li>Postal Code: ' + geoip_postal_code() + '</li>'+
        '<li>Country Code: ' + geoip_country_code() + '</li>'+
        '<li>Country Name: ' + geoip_country_name() + '</li>'+
        '</ul>';
    $("#geo-info").html(out);
});
</script>

Получаем местоположение пользователя через W3C Geo API

Точность определения местоположения через IP-адрес оставляет желать лучшего. Она, как правило, достоверно позволяет определить город. Говорить об определении улицы или даже дома просто не приходится. Другим недостатком этой техники является то, что пользователь может подключаться через прокси- или VPN-серверы, которые располагаются в совершенно других городах.

Комитет W3C предложил рекомендацию для Geo-location API, позволяющее браузеру запросить местоположение у пользователя. Она уже реализована в Firefox 3.5 и в Safari на iPhone.

// if the browser supports the w3c geo api
if (navigator.geolocation) {
    // get the current position
    navigator.geolocation.getCurrentPosition(
    // if this was successful, get the latitude and longitude
    function (position) {
        var lat = position.coords.latitude;
        var lon = position.coords.longitude;
    },
    // if there was an error
    function (error) {
        alert('ouch');
    });
}

По материалам Smashing Magazine

Оставте свой комментарий

Определение IE6 через CSS селектор

Определение версии браузера через переменную User-Agent не всегда работает правильно из-за того, что это значение легко подменить. Разработчики jQuery вообще решили отказаться от его использования, предложив как альтернативу определение особенностей браузера программно. Различные реализации одних и тех же свойств или отсутствие какой-либо реализации позволяет четко выделить Internet Explorer на фоне остальных. Но вот выделить какую-либо версию уже становится большой проблемой.

Следуя предложенной концепции выявления особенностей браузера, я решил использовать CSS селектор tag[class] для обнаружения IE6. В старших версиях IE, как и в других современных браузерах, этот селектор корректно обрабатывается, а в 6-й версии он игнорируется.

(function ($) {
    var cssCode = 'html.jQueryDetectIE6{background-color:#ffffff;}html.jQueryDetectIE6[class]{background-color:#fefefe;}',
        styleElement, html;
    try {
        html = $("html");
        if (html.length) {
            styleElement = document.createElement("style");
            styleElement.type = "text/css";
            if (styleElement.styleSheet) {
                styleElement.styleSheet.cssText = cssCode;
            } else {
                styleElement.appendChild(document.createTextNode(cssCode));
            }
            $("head").prepend(styleElement);
            html.addClass("jQueryDetectIE6");
            $.support.cssClassSelector = /254|fe/ig.test(html.css("backgroundColor"));
        }
    } catch (e) {
        // nothing here
    } finally {
        // cleanup
        if (html && html.length) {
            html.removeClass("jQueryDetectIE6");
        }
        if (styleElement && styleElement.parentNode) {
            styleElement.parentNode.removeChild(styleElement);
        }
    }
}(jQuery));

Во время своего выполнения функция добавляет необходимый стиль в тег head и проверяет их влияние на элемент. Результат проверки заносится в поле $.support.cssClassSelector.

Флаг хотелось установить еще до полной загрузки документа. По этому требовалось свести к минимуму модификацию DOM-документа. Это удалось решить через проверку цвета у корневого элемента html.

Другая проблема возникла из-за различного поведения браузеров при добавлении стилей через JavaScript. IE и Safari начинают применять эти правила только, если они были добавлены в тег head. Тут нет побочных эффектов, так как этот элемент уже существует на момент выполнения кода. Но сам элемент style нужно сформировать особым образом. В IE есть свойство styleSheet у элемента, которое позволяет работать с правилами. В свою очередь у объекта styleSheet есть поле cssText, которое позволяет получать и устанавливать текстовое представление правил. Хочу отметить, что изменить свойство cssText можно только однажды. Если все же потребуется его поменять, то нужно будет удалить старый элемент style и добавить новый с обновленными значениями.

Оставте свой комментарий

Внедряем карты Google Maps на сайт

Многие компании хотят использовать карты на своих сайтах, чтобы отметить свои офисы или точки продаж. Я уже несколько раз для этих целей использовал модуль, который написал сам.

Для хорошей организации кода используем пространство имен, выделенное специально для нашего приложения.

var APP = {};

Собственно, код модуля:

(function () {

    // TODO!!! Нужно будет получить свой уникальный ключ
    var GOOGLE_MAP_KEY = "abcdefg";

    /**
     * Карта Google
     * @param root  корневой DOMElement, в котором будет рисоваться карта
     * @param model массив маркеров
     */
    APP.Map = function (root, model) {

        var map;

        function drawMap (map, data) {

            var i, bounds = new google.maps.LatLngBounds();

            for (i = 0; i < data.length; i++) {
                bounds.extend(new google.maps.LatLng(data[i].lat, data[i].lng));
            }

            map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds));

            // создаем базовый объект маркета
            var baseIcon = new google.maps.Icon(google.maps.DEFAULT_ICON);

            for (i = 0; i < data.length; i++) {
                (function (s) {
                    var icon, marker;
                    icon = new google.maps.Icon(baseIcon);
                    icon.image = s.icon || icon.image;
                    marker = new google.maps.Marker(new google.maps.LatLng(s.lat, s.lng), icon);
                    map.addOverlay(marker);

                    google.maps.Event.addListener(marker, "click", function() {
                        // обрабатываем клики по маркеру
                    });
                })(data[i]);
            }
        };

        // загружаем карту, если это необходимо, и рисуем маркеры
        try {
            if (typeof google === "undefined") {
                $(document).one("mapLoaded", function () {
                    map = new google.maps.Map2(root);
                    map.addControl(new google.maps.SmallZoomControl());
                    $("body").bind("unload", google.maps.Unload);
                    drawMap(map, model);
                });
                $.getScript("http://www.google.com/jsapi?key=" + GOOGLE_MAP_KEY + "&callback=APP.Map.callback");
            } else {
                drawMap(map, model);
            }
        } catch (exc) {};
    };

    APP.Map.callback = function () {
        google.load("maps", "2", {
            "callback": function () {
                $(document).trigger("mapLoaded");
            },
            // опциональный параметр, задающий язык карты
            "language": "ru"
        });
    };
})();

Для нормальной работы модуля нужно получить ключ для доступа к API Google Maps и указать его в соответствующей переменной.

А теперь пример использования модуля.

// инициализируем карту тестовыми данными
$(function () {
    // центр в Челябинске
    var center = {lat: 55.16, lng: 61.4},
        mapHolder = $("#map-object").get(0),
        markers = [], i;

    if (mapHolder) {
        // случайно несколько маркеров около заданного центра
        for (i = 0; i < 10; i++) {
            markers.push({
                lat: center.lat + (Math.random() - 0.5) / 30,
                lng: center.lng + (Math.random() - 0.5) / 15
            });
        }
        markers.push(center);
        APP.Map(mapHolder, markers);
    }
});
Оставте свой комментарий

О пользе карты сайта

Карта сайта (Sitemap) — это XML-файл с информацией о страницах сайта, которые нужно проиндексировать поисковым системам. Поисковые системы могут обнаружить все страницы сайта, на которые есть ссылки, но с помощью карты сайта ему эта информация предоставляется в явном виде. В этом файле указывается местонахождение страниц сайта, время их последнего обновления, частоту обновления и важность относительно других страниц сайта для того.

Например, Sitemap может быть таким:

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    <url>
        <loc>http://noteskeeper.ru/</loc>
        <lastmod>2010-03-20</lastmod>
        <changefreq>daily</changefreq>
        <priority>1</priority>
    </url>
</urlset>

Спецификацию протокола можно найти на сайте sitemaps.org

Как альтернатива карте сайта может использоваться канал синдикации RSS или ATOM. Однако на практике в этих файлах передаются далеко не все страницы.

Оставте свой комментарий

Закомментировать код

Бывает, что комментарии в коде скрывают не описание функций, а целые куски кода. Может быть они понадобятся в следующих релизах или зависят от функционала, который еще не написан, а может быть это устаревшие части и в них уже нет необходимости. В любом случае кроме пользы это потенциальная угроза безопасности.

Тем не менее закомментированные блоки могут быть очень полезны для отладки. Один изящный трюк, позволяющий только с помощью одного символа закомментировать целый кусок кода, заключается в следующем:

/*

alert("Error!!!");

// */

Этот блок сейчас закомментирован. Но добавив всего-лишь один слеш в первой строке комментария мы раскомментируем весь блок целиком.

//*

alert("Error!!!");

// */

Этот трюк сильно упрощает включение и выключение целых блоков кода.

Комментарии к заметке: 3