Можно ли прослушать событие «изменение стиля»?

206

Можно ли создать прослушиватель событий в jQuery, который можно привязать к любым изменениям стиля? Например, если я хочу что-то «сделать», когда элемент меняет размеры, или любые другие изменения в атрибуте стиля, я мог бы сделать:

$('div').bind('style', function() {
    console.log($(this).css('height'));
});

$('div').height(100); // yields '100'

Это было бы действительно полезно.

Любые идеи?

ОБНОВИТЬ

Извините, что ответил на это сам, но я написал аккуратное решение, которое может подойти кому-то еще:

(function() {
    var ev = new $.Event('style'),
        orig = $.fn.css;
    $.fn.css = function() {
        $(this).trigger(ev);
        return orig.apply(this, arguments);
    }
})();

Это временно переопределит внутренний метод prototype.css и переопределит его с помощью триггера в конце. Так что это работает так:

$('p').bind('style', function(e) {
    console.log( $(this).attr('style') );
});

$('p').width(100);
$('p').css('color','red');
Дэвид Хеллсинг
источник
да, отличное решение, но в реальном приложении, я подозреваю, это может потребовать много ресурсов, и даже заставить браузер работать медленно, чтобы не отставать от взаимодействия с пользователем. Я хотел бы знать, если это так или нет.
пиксель
1
@pixeline: Я не вижу, что это должно быть намного медленнее. В любом случае, jQuery все еще вызывает прототип css, я просто добавляю к нему триггер. Таким образом, это всего лишь один дополнительный вызов метода.
Дэвид Хеллсинг
Я понимаю это: просто кажется, что в потоке пользователя, взаимодействующего с приложением, css () вызывается много времени. В зависимости от того, что вы делаете с этим триггером (если это просто журнал консоли, я думаю, вы, конечно, в безопасности), это может создать проблемы. Мне просто интересно. Это зависит от приложения тоже. Лично я склонен делать много визуальных отзывов о своих вещах.
пиксель
Есть еще пара вещей, которые нужно сделать, чтобы он работал с существующими вызовами jquery css: 1) вы захотите вернуть элемент из вашей новой функции css, иначе он прервет беглые вызовы css стиля. например. $ ('p'). css ('display', 'block'). css ('color', 'black'); и т.д. 2) если это вызов значения свойства (например, 'obj.css (' height ');'), вы хотите вернуть возвращаемое значение исходной функции - я делаю это, проверяя, есть ли список аргументов для функции содержит только один аргумент.
theringostarrs
1
Я сделал более надежную версию этого подхода, который также работает при удалении самого атрибута стиля . Это вызовет событие 'style' для каждого стиля, который удаляется таким образом. gist.github.com/bbottema/426810c21ae6174148d4
Бенни Боттема

Ответы:

19

Поскольку jQuery с открытым исходным кодом, я бы предположил, что вы можете настроить cssфункцию так, чтобы она вызывала функцию по вашему выбору каждый раз, когда она вызывается (передавая объект jQuery). Конечно, вы захотите просмотреть код jQuery, чтобы убедиться, что внутри него ничего больше не используется для установки свойств CSS. В идеале вы хотели бы написать отдельный плагин для jQuery, чтобы он не влиял на саму библиотеку jQuery, но вам придется решить, возможно ли это для вашего проекта.

Джош Стодола
источник
1
Но что в этом случае произойдет, если я установлю свойство стиля прямо с помощью функций DOM?
Хайме Хаблутцель
Вот полный пример этого решения: gist.github.com/bbottema/426810c21ae6174148d4
Бенни Боттема
272

С тех пор, как вопрос был задан, дела пошли немного дальше - теперь можно использовать MutationObserver для обнаружения изменений в атрибуте 'style' элемента, jQuery не требуется:

var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutationRecord) {
        console.log('style changed!');
    });    
});

var target = document.getElementById('myId');
observer.observe(target, { attributes : true, attributeFilter : ['style'] });

Аргумент, который передается в функцию обратного вызова, является объектом MutationRecord, который позволяет вам получить старые и новые значения стиля.

Поддержка хороша в современных браузерах, включая IE 11+.

CodeBox
источник
19
Имейте в виду, что это работает только со встроенными стилями, а не тогда, когда стиль изменяется как следствие изменения класса или @mediaизменения.
fregante
2
@ bfred.it у вас есть идея, как этого добиться? Я хочу запустить несколько js после того, как правила класса применены к элементу.
Крагалон
Вы можете использовать getComputedStyleс, setIntervalно это сильно замедлит ваш сайт.
Фреган
Примечание: MutationObserver на самом деле не работает во всех версиях Android 4.4, несмотря на то, что говорит caniuse.
Джеймс Гулд
Посмотрите этот тиддл для примера изменения цвета фона текстовой области на синий, когда ширина текстовой области растягивается на 300 пикселей. jsfiddle.net/RyanNerd/y2q8wuf0 (к сожалению, в FF, похоже, есть ошибка, которая приводит к зависанию скрипта вHTMLElement.style.prop = "value"
RyanNerd
18

Объявление вашего объекта события должно быть внутри вашей новой функции CSS. В противном случае событие может быть запущено только один раз.

(function() {
    orig = $.fn.css;
    $.fn.css = function() {
        var ev = new $.Event('style');
        orig.apply(this, arguments);
        $(this).trigger(ev);
    }
})();
Майк Аллен
источник
Отлично, спасибо. Некоторые люди говорят, изменить исходный код, но ваше расширение является лучшим. У меня были некоторые проблемы, но, наконец, это работает.
ccsakuweb
Он непрерывно стреляет, выполняя этот код, и даже не достигает требуемого результата.
smkrn110
13

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

расширение

// Extends functionality of ".css()"
// This could be renamed if you'd like (i.e. "$.fn.cssWithListener = func ...")
(function() {
    orig = $.fn.css;
    $.fn.css = function() {
        var result = orig.apply(this, arguments);
        $(this).trigger('stylechanged');
        return result;
    }
})();

использование

// Add listener
$('element').on('stylechanged', function () {
    console.log('css changed');
});

// Perform change
$('element').css('background', 'red');

Я получил ошибку, потому что var ev = new $ .Event ('style'); Нечто похожее на стиль не было определено в HtmlDiv. Я удалил его и теперь запускаю $ (this) .trigger ("stylechanged"). Другая проблема заключалась в том, что Майк не возвращал результат $ (css, ..), тогда он может создавать проблемы в некоторых случаях. Итак, я получаю результат и возвращаю его. Теперь работает ^^ В каждом изменении CSS есть некоторые библиотеки, которые я не могу изменить, и вызвать событие.

ccsakuweb
источник
Очень услужливый и умный
dgo
5

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

$('#blah').bind('height-changed',function(){...});
...
$('#blah').css({height:'100px'});
$('#blah').trigger('height-changed');

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

droo
источник
Это действительно очень удобное решение, если у вас есть доступ к коду, который меняет стили.
Umair Hafeez
2

В jQuery или java-скрипте нет встроенной поддержки события изменения стиля. Но jQuery поддерживает создание пользовательских событий и их прослушивание, но каждый раз, когда происходит изменение, у вас должен быть способ вызвать его на себя. Так что это не будет полным решением.

Теджа Кантамнени
источник
2

Вы можете попробовать плагин Jquery, он запускает события, когда CSS меняется и его легко использовать

http://meetselva.github.io/#gist-section-attrchangeExtension
 $([selector]).attrchange({
  trackValues: true, 
  callback: function (e) {
    //console.log( '<p>Attribute <b>' + e.attributeName +'</b> changed from <b>' + e.oldValue +'</b> to <b>' + e.newValue +'</b></p>');
    //event.attributeName - Attribute Name
    //event.oldValue - Prev Value
    //event.newValue - New Value
  }
});
user889030
источник
1

Интересный вопрос. Проблема в том, что height () не принимает обратный вызов, поэтому вы не сможете запустить обратный вызов. Используйте animate () или css (), чтобы установить высоту, а затем вызвать пользовательское событие в обратном вызове. Вот пример использования animate (), испытания и работы ( демо ) в качестве доказательства концепции:

$('#test').bind('style', function() {
    alert($(this).css('height'));
});

$('#test').animate({height: 100},function(){
$(this).trigger('style');
}); 
pixeline
источник
8
Извините, но что это доказало? Вы trigger()событие вручную. Я думаю, что OP происходит после какого-то автоматически запускаемого события, когда атрибут стиля изменяется.
JPot
@JPot он показывает, что атрибут высоты не генерирует события при изменении.
AnthonyJClink
1

Просто добавляем и формализуем решение @David сверху:

Обратите внимание, что функции jQuery являются цепочечными и возвращают 'this', так что несколько вызовов могут вызываться один за другим (например, $container.css("overflow", "hidden").css("outline", 0); ).

Таким образом, улучшенный код должен быть:

(function() {
    var ev = new $.Event('style'),
        orig = $.fn.css;
    $.fn.css = function() {
        var ret = orig.apply(this, arguments);
        $(this).trigger(ev);
        return ret; // must include this
    }
})();
Нир Хемед
источник
0

Как насчет jQuery cssHooks?

Может быть, я не понимаю вопроса, но то, что вы ищете, легко сделать cssHooks, не меняяcss() функции.

копия из документации:

(function( $ ) {

// First, check to see if cssHooks are supported
if ( !$.cssHooks ) {
  // If not, output an error message
  throw( new Error( "jQuery 1.4.3 or above is required for this plugin to work" ) );
}

// Wrap in a document ready call, because jQuery writes
// cssHooks at this time and will blow away your functions
// if they exist.
$(function () {
  $.cssHooks[ "someCSSProp" ] = {
    get: function( elem, computed, extra ) {
      // Handle getting the CSS property
    },
    set: function( elem, value ) {
      // Handle setting the CSS value
    }
  };
});

})( jQuery ); 

https://api.jquery.com/jQuery.cssHooks/

halfbit
источник
-1

У меня была такая же проблема, поэтому я написал это. Это работает довольно хорошо. Отлично смотрится, если смешать это с некоторыми переходами CSS.

function toggle_visibility(id) {
   var e = document.getElementById("mjwelcome");
   if(e.style.height == '')
      e.style.height = '0px';
   else
      e.style.height = '';
}
KingLouie
источник