Javascript-форум (https://javascript.ru/forum/)
-   Events/DOM/Window (https://javascript.ru/forum/events/)
-   -   Динамическое применение стилей к элементу и их отмена (https://javascript.ru/forum/events/33521-dinamicheskoe-primenenie-stilejj-k-ehlementu-i-ikh-otmena.html)

Amphiluke 26.11.2012 16:09

Динамическое применение стилей к элементу и их отмена
 
Всем привет. Вопрос по динамическому применению стилей к элементам.

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

Всё просто, если значения CSS-свойств известны «at design time» (т.е. статичны): в таблице стилей создается CSS-класс с перечнем нужных CSS-свойств со статичными значениями, и модуль решает задачу добавлением/удалением имени класса в атрибуте className.
.MyModuleStyle {
    width:150px;
}

elem.classList.add("MyModuleStyle");
// ...
elem.classList.remove("MyModuleStyle");


А что если желаемые значения CSS-свойств становятся известны только во время выполнения? С одной стороны, можно было бы перед применением стиля запомнить прежнее значение свойства, а потом восстановить его.
var desiredWidth = 150, // условно. В действительности значение становится известно in runtime
    oldWidth = window.getComputedStyle(elem, null).width;
elem.style.width = desiredWidth + "px";
// ...
elem.style.width = oldWidth;

Это решение, казалось бы, работает, но оно совершенно неприемлемо для модуля, т.к. возможные последующие попытки пользователя динамически поменять ширину элемента путем назначения какого-нибудь своего CSS-класса при этом будут обречены на провал (стили класса не перекроют инлайновые, которые мы добавили в последней строке примера). Сделать elem.removeAttribute("style") — для модуля тоже не вариант, естественно.

Мне видится только один возможный вариант решения: манипуляции с таблицами стилей напрямую через JavaScript. Из скрипта создаем (CSSStyleSheet.insertRule) CSS-правило для класса, заполняем значения свойств динамически вычисленными значениями и далее оперируем атрибутом className. Но выглядит это решение, на мой взгляд, как «костыль».

Может, кто-нибудь встречался с подобными проблемами? Как выходили из положения?

danik.js 26.11.2012 16:34

Почему же костыль. Это именно решение проблемы. А как еще можно применить стили, да так, чтоб они были перекрываемы другими стилями? Через атрибут style конечно же будут назначены наиболее приоритетные свойства (я не принимаю во внимание !important). Значит нужно назначать свойства через stylesheet.

Amphiluke 26.11.2012 17:30

Возможно, с «костылем» погорячился, :) просто порой используя все возможности DOM и интерфейса CSSStyleSheet, можно довести степень ненавязчивости JavaScript до абсурда, когда, утрируя, скрипт будет создавать под себя всю HTML-разметку, весь CSS (да и свой собственный код заодно).

Впрочем, наверное, вы правы. Если проблема явно ложится в рамки возможностей и предназначения интерфейса CSSStyleSheet, то решение, по-видимому, приемлемо. Жаль, что оно единственно… наверное.

melky 26.11.2012 17:36

Цитата:

Сообщение от Amphiluke
А что если желаемые значения CSS-свойств становятся известны только во время выполнения? С одной стороны, можно было бы перед применением стиля запомнить прежнее значение свойства, а потом восстановить его.

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

небольшая выдержка, чтобы не искать самому :

var stylesheet = document.createElement("style");
    document.getElementsByTagName("script")[0].parentNode.appendChild(stylesheet);
    stylesheet = stylesheet.sheet || stylesheet.styleSheet;


    /**
     * Добавит правило с указанным селектором и указанным текстом правила.
     * @param selector
     * @param cssText
     * @return {CSSRule} Добавленное правило
     */
    function addRule (selector, cssText) {

        /** @type {CSSRuleList} */
        var rules = stylesheet.cssRules || stylesheet.rules;
        var index = rules.length;

        if (stylesheet.insertRule) {
            stylesheet.insertRule(selector + " " + "{" + cssText + "}", index);
        } else {
            stylesheet.addRule(selector, cssText, rules.length);
        }

        return rules[index];
    }


С удалением правил из таблицы стилей я разбирался в этой теме : http://javascript.ru/forum/164679-post8.html.

tenshi 26.11.2012 18:46

var desiredWidth = 150, // условно. В действительности значение становится известно in runtime
elem.style.width = desiredWidth + "px"; // переопределяем
// ...
elem.style.width = ''; // отменяем переопределение

Amphiluke 26.11.2012 19:02

tenshi, спасибо, но не подходит. Просто в силу того, что модуль должен сбрасывать только свои собственные стили (в любом окружении). Если пользователь сам пропишет (или CMS сгенерирует) инлайновые стили в теге, мы затрем их, если будем использовать ваш способ.

nerv_ 26.11.2012 21:12

Amphiluke, еще вариант: написать все возможные(требуемые) варианты стилей и подключить их внешним файлом. Так все виджеты делают :) В итоге, пользователь загрузить его (файл стилей) один раз, а в дальнейшем будет поднимать из кеша.

Amphiluke 26.11.2012 21:36

nerv_, а как быть, если множество вариантов составляет множество мощности континуум? :D Ну вот как в примере с шириной. Она рассчитывается динамически и зависит, к примеру, от того, каков дизайн и метрика целевой страницы.

P.S. Пример это не надуманный, в реальном проекте проблема стоит как раз примерно так.

P.P.S. В общем, на данный момент реализовал это дело так, как в примере у melky. Видимо, так оно пока и останется до следующей глобальной архитектурной перестройки модуля. :) Спасибо всем за идеи.

tenshi 26.11.2012 22:25

Цитата:

Сообщение от Amphiluke (Сообщение 217985)
tenshi, спасибо, но не подходит. Просто в силу того, что модуль должен сбрасывать только свои собственные стили (в любом окружении). Если пользователь сам пропишет (или CMS сгенерирует) инлайновые стили в теге, мы затрем их, если будем использовать ваш способ.

и правильно сделаем, ибо нефиг)

tenshi 26.11.2012 22:26

var desiredWidth = 150, // условно. В действительности значение становится известно in runtime
    oldWidth = elem.style.width;
elem.style.width = desiredWidth + "px";
// ...
elem.style.width = oldWidth;


Часовой пояс GMT +3, время: 05:15.