Статьи

Чтение элементов микроданных в Chrome

Прежде чем идти дальше, обратите внимание, что этот блог определенно попадает в категорию «сомнительных». Пожалуйста, прочитайте следующее с большим количеством соли (и холодного пива на вашей стороне). Недавно я прочитал несколько статей о микроданных. Сегодня я прочитал еще один хороший пример: сделайте свою страницу удобной для использования роботами и людьми с помощью микроданных .

Концепция довольно проста. Встраивая немного метаданных в ваш код, вы создаете для своих страниц машиночитаемый контекст. Это немного похоже на атрибуты данных, но, на мой взгляд, немного по-другому. Атрибуты данных, на мой взгляд, полезны для данных автономным способом. Т.е. вы размечаете свои страницы, чтобы ваш код (JavaScript или CSS) мог что-то с этим сделать. Микроданные предназначены для внешних потребителей. В сочетании с внешними схемами это может быть довольно мощным. Судя по всему, Google уже использует это, так что это имеет определенную ценность для SEO.

Я заинтересовался еще больше, когда увидел, что для этого есть DOM API: document.getItems (). Предположительно, это вернет все элементы микроданных в вашем текущем документе. К сожалению, это не удалось в Chrome. Удивительно, но CanIUse.com не смог сообщить об API, и мне пришлось еще немного покопаться, чтобы выяснить, что — по-видимому — только Firefox и Opera поддерживают этот API на данный момент.

Я хотел создать что-то, что а) замечало бы, использовались ли микроданные, и б) сообщало, как оно использовалось. Я знал, что могу перебрать все элементы в DOM и повторить, но я предположил, что это будет довольно расточительно. Затем я обнаружил функцию document.evaluate . Это позволяет использовать XPath для поиска в DOM. Поэтому, имея в своем распоряжении, я сначала создал функцию, которая будет проверять наличие любых используемых микроданных:

function hasItems() {
    return document.evaluate("count(/html/body//*[@itemscope])", document, null, XPathResult.NUMBER_TYPE, null).numberValue > 0;
}

Если вы не читали статью, на которую я ссылался ранее, использование itemscope в качестве атрибута «оборачивает» элементы DOM, которые считаются одной логической единицей микроданных. Мой XPath просто ищет это и запускает операцию count (), чтобы получить количество совпадающих элементов.

Затем я написал функцию, которая будет возвращать эти элементы. По большей части, это простой вопрос итерации по результатам XPath и использования функций DOM для получения значений, но вы должны использовать немного логики в зависимости от того, с каким типом узла DOM вы имеете дело. Например, если для свойства используется тег Anchor, то значение микроданных определяется атрибутом href. Для большинства других вещей вы просто используете внутренний текст. Вот моя функция getItems (и да, это имя слишком общее):

function getItems() {
    var items = document.evaluate("/html/body//*[@itemscope]", document, null, XPathResult.ANY_TYPE, null); 
    var results = [];
    var result = items.iterateNext();
    while(result) {
        var kids = document.evaluate(".//*[@itemprop]", result, null, XPathResult.ANY_TYPE, null); 
        var item = {};
        var kidprop = kids.iterateNext();
        while(kidprop) {
            var attr = kidprop.attributes.getNamedItem("itemprop");
            //To get the value, it depends on the type
            var value="";
            switch(kidprop.nodeName) {
                case "AREA":
                case "LINK":
                case "A":
                    value = kidprop.href;
                    break;

                case "AUDIO":
                case "EMBED":
                case "IFRAME":
                case "IMG":
                case "SOURCE":
                case "VIDEO":
                    value = kidprop.src;
                    break;

                default: 
                    value = kidprop.innerText;
                    break;
            }
            item[attr.nodeValue] = value;
            kidprop = kids.iterateNext();
        }

        results.push(item);
        result = items.iterateNext();
    }
    return results;
}

Я использовал исходный HTML на основе статьи, на которую я ссылался ранее:

<ul>
<li itemscope>
    <ul>
        <li>Name: <span style="foo" itemprop="name2">Fred</span></li>
        <li>Name: <span itemprop="name">Fred</span></li>
        <li>Phone: <span itemprop="telephone">210-555-5555</span></li>
        <li>Email: <span itemprop="email">[email protected]</span></li>
        <li>Site: <a href="foo.html" itemprop="url">My site</a></li>
    </ul>
</li>
<li itemscope>
    <ul>
        <li>Name: <span itemprop="name">Wilma</span></li>
        <li>Phone: <span itemprop="telephone">210-555-7777</span></li>
        <li>Email: <span itemprop="email">[email protected]</span></li>
    </ul>
</li>
<li itemscope>
    <ul>
        <li>Name: <span itemprop="name">Betty</span></li>
        <li>Phone: <span itemprop="telephone">210-555-8888</span></li>
        <li>Email: <span itemprop="email">[email protected]</span></li>
    </ul>
</li>
<li itemscope>
    <ul>
        <li>Name: <span itemprop="name">Barny</span></li>
        <li>Phone: <span itemprop="telephone">210-555-0000</span></li>
        <li>Email: <span itemprop="email">[email protected]</span></li>
    </ul>
</li>
</ul>

Когда я выполняю свой JavaScript против этого, я получаю:

Полезно? Пока не уверен. Я предполагаю, что в конце концов Chrome все равно получит нативный API. (Хотя в Firefox он возвращает элементы Node, а не такой хороший массив, как у меня, если я его не использую неправильно, похоже, что все еще может быть необходимость в служебной функции.)