Заметки в категории «JavaScript» (страница 11)

Обнаружение браузера Opera 9

Совсем недавно я начал применять web-шрифты в реальных проектах. Благодаря простым утилитам (например, @font-face Generator ) можно быстро сконвертировать шрифт в нужные форматы. Но статья совсем не о том. Эти web-шрифты прекрасно (если не брать во внимание проблемы со сглаживанием) отображаются во всех браузерах (даже в IE6!!!) кроме Opera версии 9 и ниже. В связи с эти фактом я и озадачился методами обнаружения Opera 9.

Очевидным способом является проверка строчки User-Agent . Этот вариант решения проблемы хорош до тех пор, пока не сталкиваешься с подменой строки.

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

Так для браузеров Opera до 10 версии будет характерным отличительным признаком отсутствие события contextmenu у DOM-элемента.


function () {
  var isPresent = null;
  if (document.createElement) {
    var el = document.createElement("p");
    if (el && el.setAttribute) {
      el.setAttribute("oncontextmenu", "");
      isPresent = typeof el.oncontextmenu != "undefined";
    }
  }
  return isPresent;
}

Обнаружение такой не очевидной особенности гораздо эффективнее, например, по сравнению с обнаружением поддержки web-шрифтов потому, что шрифту придется загрузить по сети.

Так же, наверное, можно использовать какие-то CSS-хаки, специфичные для Opera, по аналогии с тем, как я делал определение IE6.

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

Sizzle задом наперед

В состав jQuery входит очень мощная библиотека Sizzle , которая отвечает за получение списка DOM-элементов, соответствующих заданным условиям. В ней реализованы все селекторы CSS3 и некоторые другие, которые не включены в спецификацию.

Хотя в некоторых современных браузерах у DOM-элементов есть специальные методы querySelector и querySelectorAll , которые реализуют этот функционал, тем не менее актуальность этой библиотеки остаётся неизменной из-за поддержки старых браузеров и браузеров, в которых нет эти методов.

Чтобы полностью раскрыть скоростной потенциал Sizzle нужно понимать, как на самом деле происходит фильтрация элементов документа.

Хорошим тоном при составлении правила будет первым селектором использовать получение элемента по атрибуту id , так как эта функция работает очень быстро и реализована во всех браузерах. Как вариант, можно задавать контекст поиска, если он известен, что сильно сокращает количество элементов, которые придется перебирать.

$("#data div.frame p span.dashed");

$("#data").find("div.frame p span.dashed");

$("div.frame p span.dashed", $("#data"));

В каждом правиле (за исключением поиска по id) стоит указывать тег, которому оно соответствует. Функция поиска по тегу тоже присутствует по всех браузерах. Так запрос

$("div.frame p span.dashed");

выполнится быстрее чем

$(".frame p .dashed");

Поиск по значению класса тоже уже реализован во многих браузерах, но в них, скорее всего, будет использоваться метод querySelectorAll.

А теперь самое интересное. Поиск элементов в документе или указанном контексте начинается с самого последнего правила. Затем уже отбрасываются элементы, предки которых не соответствуют требуемым условиям.

$("div.frame p span.dashed");

В этом примере сначала будут найдены все элементы span в документе. Затем отфильтрованы элементы, которые содержат класс dashed . После этого для каждого найденного элемента будет предпринята попытка найти у него предка с тегом p, затем предка с тегом div и классом frame.

Такой, казалось бы, не очевидный подход позволяет резко сократить количество операций на поиск элементов.

Как и везде, в алгоритме Sizzle есть исключение. Запрос типа $("#data span.dashed") будет обработан особым образом — селекторы в этом случае традиционно применятся слева направо.

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

JSONP для инициализации страницы

Блоки с динамическим содержимым уже давно принято обновляться асинхронными запросами на сервер. А вот для наполнения их начальными данными эта техника не слишком хорошо подходит из-за видимой задержки в появлении внешнего оформления блока и самого содержимого блока. Чтобы минимизировать эту задержку данные уже должны находиться на странице во время ее загрузки. Очевидно, что при этом HTML захламляется вкраплениями JavaScript, что не слишком хорошо. Компромиссным вариантом может быть загрузка данных с помощью тега script.

Предположим, что на сервере есть контроллер, который отдает модель данных в виде JSON. Пусть URL, по которому можно получить эти данные, будет http://example.com/data/?page=1, а сами данные


{sites: [
    {id: 1, name: "Yandex.ru", url: "http://yandex.ru/"},
    {id: 2, name: "Google", url: "http://google.com/"},
    {id: 3, name: "NotesKeeper", url: "http://noteskeeper.ru/"}
]}

Этот URL используется в асинхронном запросе, чтобы получить новую страницу блока. Если его подставить в тег script , то он, конечно же, будет загружен и исполнен, но возникнет синтаксическая ошибка, так как объект не был использован в операции присвоения или при вызове функции.

Добавим в параметры к URL еще один http://example.com/data/?page=1&callback=App.render и модифицируем контроллер на сервере так, чтобы этот параметр (при его наличии) подставлялся в выходные данные


App.render( {sites: [
    {id: 1, name: "Yandex.ru", url: "http://yandex.ru/"},
    {id: 2, name: "Google", url: "http://google.com/"},
    {id: 3, name: "NotesKeeper", url: "http://noteskeeper.ru/"}
]} );

Теперь такой адрес можно использовать в теге script. Когда данные будут загружены, то автоматически произойдет вызов указанной функции с моделью.

Контроллер интерфейса может иметь, например, такую структуру


var App = (function () {
    // скрытые переменные приложения
    return {
        // публичные методы приложения
        render: function (model) {
            // действия, которые можно выполнить до полной загрузки документа
            $(function () {
                // изменения в документе, который выполняются только
                // после его полной загрузки
                App.update(model);
            });
        },
        update: function (model) {
            // обновляем содержимое блока
        }
    };
}());

Когда мы получим от сервера модель с начальными данными, то дождемся сигнала о полной загрузке документа, чтобы безопасно можно было начать его изменения, и приступим к построению содержимого блока.

Эта техника и называется JSONP (JSON with padding). Основное применение она нашла в загрузке данных сторонних сервисов, например, таких как Flickr, Yahoo, Google и многих других. Из-за того, что асинхронные запросы для других доменов блокируются политикой безопасности браузера, использование этой техники наряду с iframe является, пожалуй, единственным возможным вариантом.

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

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

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

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

Сопоставляем 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 и добавить новый с обновленными значениями.

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