пятница, 15 июня 2012 г.

JavaScript ToolTip’ы для SharePoint

У вас есть некоторая страница, и вам нужно показать всплывающую при наведении на некий элемент. Как вы это сделаете?
Мне кажется, абсолютное большинство - пойдет в Google и напишет что-нибудь типа “jQuery tooltip”.
Когда я лично сталкиваюсь с такими задачами, я всегда первым делом стараюсь вспомнить, где я видел всплывающие подсказки в SharePoint? И на ум сразу же приходит Ribbon!
image
Причем, подсказки на Ribbon могут выглядеть даже еще красивее:
image
Возникает естественный вопрос: почему бы не использовать такие же всплывающие подсказки в своих собственных решениях? Это знакомый пользователям интерфейс, который отлично сочетается с общим дизайном портала. Да и вообще, эти подсказки действительно красивые.
Мне удалось это сделать, и получить вот такие всплывающие подсказки на моих собственных страницах:
image
Ниже я расскажу, как этого можно добиться.

Исследование

Кому не интересны исследования, могут безболезненно пролистать вниз до заголовка “Решение”, взять оттуда готовый код, вставить его в свои решения, и радоваться жизни :)
Кто остался, поступят не менее мудро: всё-таки в SharePoint многое зависит от того, насколько хорошо ты умеешь исследовать, и опыт успешных исследований всегда ценен. Как минимум, я такие вещи читаю с удовольствием.
В общем, как вы наверное уже догадались, никакой документации по использованию всплывающих подсказок на MSDN нет. Вполне ожидаемо… Пространство имен CUI, кстати, по версии MSDN содержит единственный класс JsonXmlElement :)
image
На самом деле, там есть еще куча классов, и среди них – интересующий нас CUI.ToolTip.
Однако, использовать его оказывается не так-то просто.
Конструктор выглядит следующим образом:
CUI.ToolTip = function(root, id, title, description, properties)

Всё понятно с title и description, но что за root и что за properties? Оказывается, в качестве root обязательно нужно передавать специальный “корневой” компонент (CUI.Component, обычно это корневой компонент Ribbon на странице), с кучей каких-то заполненных свойств.

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

Поэтому, поковырявшись минут 20 с попытками создания CUI.ToolTip, я решил попробовать пойти другим путем, и посмотреть, откуда используется CUI.ToolTip.

Оказывается, класс CUI.Control, который является базовым классом для всех контролов на Ribbon’е, содержит метод launchToolTip. Этот метод автоматически создает и отображает всплывающую подсказку, согласно свойствам контрола, а эти свойства передаются в конструктор CUI.Control и весьма прозрачны:
CUI.ControlProperties.prototype = {
    Command: null,
    Id: null,
    TemplateAlias: null,
    ToolTipDescription: null,
    ToolTipHelpKeyWord: null,
    ToolTipImage32by32: null,
    ToolTipImage32by32Class: null,
    ToolTipImage32by32Top: null,
    ToolTipImage32by32Left: null,
    ToolTipSelectedItemTitle: null,
    ToolTipShortcutKey: null,
    ToolTipTitle: null,
    LabelCss: null}

Однако, конструктор CUI.Control по-прежнему требует передачи ему root-объекта, и вообще весь CUI.Control очень уж заточен под Ribbon. К тому же, это будет означать, что мы не сможем показывать всплывающие подсказки на произвольных html элементах, а необходимость все обертывать в CUI.Control это довольно неудобно. Поэтому я решил не тратить больше времени на исследования и вынужден был признать, что совсем стандартными средствами показать всплывающую подсказку мне не удастся.

С другой стороны, я нашел код который показывает стандартные всплывающие подсказки, и кто запрещает этот код хотя бы скопировать и использовать в собственном классе?..

Решение


Итак, решение заключается в создании простенького класса, который отображает всплывающие подсказки аналогично тому, как это делает стандартный класс CUI.ToolTip. Оригинальный сгенерированный код выглядел, мягко сказать, “не очень”, так что я его, скажем так, написал заново. В итоге получился вот такой класс:
/// <reference name="MicrosoftAjax.js" />
Type.registerNamespace("My.Namespace");

My.Namespace._ToolTipManager = function () {

    var _divId = "my_tooltip";
    var _innerDivId = "my_tooltip_inner";
    this.attachToolTip = function (element, title, description) {
        $addHandler(element, 'mouseover', function (e) { showTooltip(element, createTitleAndDescriptionHtml(title, description)); });
        $addHandler(element, 'mouseout', hideDiv);
    }

    this.attachToolTipRaw = function (element, rawHtml) {
        $addHandler(element, 'mouseover', function (e) { showTooltip(element, rawHtml); });
        $addHandler(element, 'mouseout', hideDiv);
    }
    function createTitleAndDescriptionHtml(title, description) {
        return String.format(
            '<div class="ms-cui-tooltip-title">{0}</div><div class="ms-cui-tooltip-description">{1}</div>',
            title,
            description);
    }

    function showTooltip(element, rawHtml) {

        var tooltipDiv = $get(_divId);
        if (tooltipDiv == null)
            tooltipDiv = createTooltip();

        $get(_innerDivId).innerHTML = rawHtml;

        displayTooltipNextToElement(tooltipDiv, element);
    }

    function displayTooltipNextToElement(tooltipDiv, element) {

        tooltipDiv.style.display = '';
        var loc = Sys.UI.DomElement.getLocation(element);
        tooltipDiv.style.left = loc.x + 'px';
        tooltipDiv.style.top = loc.y + element.offsetHeight + 2 + 'px';

        if (tooltipDiv.curTimeout != null)
            clearTimeout(tooltipDiv.curTimeout);
    }

    function createTooltip() {
        var mainDiv = document.createElement('span')
        mainDiv.id = _divId;
        mainDiv.className = 'ms-cui-tooltip';
        mainDiv.style.width = 'auto';
        mainDiv.style.position = 'absolute';

        var bodyDiv = document.createElement('div');
        bodyDiv.className = 'ms-cui-tooltip-body';
        bodyDiv.style.width = 'auto';

        var innerDiv = document.createElement('div');
        innerDiv.id = _innerDivId;
        innerDiv.className = 'ms-cui-tooltip-glow';
        innerDiv.style.width = 'auto';

        bodyDiv.appendChild(innerDiv);

        mainDiv.appendChild(bodyDiv);

        document.body.appendChild(mainDiv);

        return mainDiv;
    }
    function hideDiv() {
        $get(_divId).style.display = 'none';
    }
}

My.Namespace._ToolTipManager.registerClass("My.Namespace");
My.Namespace.ToolTipManager = new My.Namespace._ToolTipManager();

На самом деле класс небольшой, особенно в сравнении с оригиналом. Логика работы, надеюсь, очевидна даже без комментариев. Написано с использованием ASP.Net Ajax, который уже включен в SharePoint и как следствие, ничего подключать для работы этого скрипта не требуется.

Обратите внимание: назначение автоматического растягивания по ширине (*.style.width = 'auto';) сделано не случайно. Если этот код убрать, всплывающая подсказка будет фиксированной ширины (около 200 пикселов).

Пример использования:
DW.DigitalSignature.ToolTipManager.attachToolTip($get('myElement'),'Помощь','Это тестовая всплывающая подсказка!');

Этот вызов приведет к тому, что каждый раз при проведении мышкой над элементом с id=myElement, будет всплывать подсказка.

Если вам необходимо использовать более сложные шаблоны, вы можете использовать метод attachToolTipRaw, куда в качестве параметра можно передать строку с HTML-кодом.

Пример более сложного шаблона:
      <div class="ms-cui-tooltip-footer">
        <span class="ms-cui-img-16by16 ms-cui-img-cont-float">
          <img style="vertical-align: top;" src="/Style Library/pdf.png" alt="" />
        </span>
        <div>
          {0}
        </div>
      </div>
      <div class="ms-cui-tooltip-clear">
      </div>
      <hr />
      <div class="ms-cui-tooltip-description">
        {1}
      </div>      <div class="ms-cui-tooltip-clear">
      </div>
      <hr />
      <div class="ms-cui-tooltip-footer">
        <span class="ms-cui-img-16by16 ms-cui-img-cont-float">
          <img style="vertical-align: top;" src="/Style Library/settings.png" alt="" />
        </span>
        <div>
          {2}
        </div>
      </div>    </div>

Где {0}, {1} и {2} должны быть заменены некоторым текстом. Замена, напомню, может быть легко произведена с помощью функции String.format :)

Этот шаблон позволяет вывести вот такую всплывающую подсказку:

image

В общем, надеюсь, этот код сэкономит кому-нибудь немного времени, и позволит сделать ваши решения более красивыми и более “SharePoint’овскими” :)

P.S. Если что-то непонятно или не получается, обязательно спрашивайте в комментариях к статье. Ну и если вдруг еще кто-то не подписан на блог, подписывайтесь, чего ждать-то :)

1 комментарий:

  1. Не могли бы вы меня послать...
    Где можно почитать про классы? Скопировал этот класс в отдельный js-файл. Подключил его на странице. В FireBug получил следующую ошибку:
    Sys.ArgumentException: Sys.ArgumentException: Значение не является именем регистрируемого типа или именем, которое является зарезервированным словом. Имя параметра: typeName
    [Прерывать на этой ошибке]

    if (parsedName !== this) throw Error.argument('typeName', Sys.Res.badTypeName);

    ОтветитьУдалить

Внимание! Реклама и прочий спам будут беспощадно удаляться.