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

Можно ли создать прослушиватель событий в 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');

10 ответов

Решение

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

С тех пор, как вопрос был задан, дела пошли немного дальше - теперь можно использовать 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+.

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

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

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

расширение

// 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 есть некоторые библиотеки, которые я не могу изменить, и вызвать событие.

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

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

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

Вы можете попробовать плагин 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
  }
});

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

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

$('#test').animate({height: 100},function(){
$(this).trigger('style');
}); 

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

Просто добавляем и формализуем решение @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
    }
})();

Как насчет 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/

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

function toggle_visibility(id) {
   var e = document.getElementById("mjwelcome");
   if(e.style.height == '')
      e.style.height = '0px';
   else
      e.style.height = '';
}
Другие вопросы по тегам