Чи можна слухати подію "Зміна стилю"?


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 () називається багато часу. Залежно від того, що ви робите з цим тригером (якщо це просто журнал консолі, я думаю, ви впевнені, що це безпечно), це може створити проблеми. Мені просто цікаво. Це також залежить від програми. Я особисто схильний робити багато візуальних відгуків у своїх речах.
пікселін

Є ще кілька речей, щоб змусити його працювати з існуючими викликами css jquery: 1) ви хочете повернути елемент з нової функції css, інакше він порушить вільні виклики css стилю. напр. $ ('p'). css ('дисплей', 'блок'). css ('колір', 'чорний'); і т.д. 2) якщо це виклик значення властивості (наприклад, 'obj.css (' висота ');'), ви хочете повернути повернене значення вихідної функції - я це роблю, перевіряючи, чи список аргументів оскільки функція містить лише один аргумент.
theringostarrs

1
Я зробив більш надійну версію цього підходу, яка також працює, коли видаляється сам атрибут стилю . Він кине подію "стилю" для кожного стилю, який видаляється таким чином. 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+.


19
Майте на увазі, що це працює лише з вбудованими стилями, а не тоді, коли стиль змінюється внаслідок зміни або @mediaзміни класу .
fregante

2
@ bfred.it Ви б мали уявлення, як саме цього досягти? Я хочу запустити деякі js після того, як до елемента будуть застосовані правила css класу.
Крагалон

Ви могли б використовувати getComputedStyleз , setIntervalале це буде сповільнювати ваш сайт багато.
fregante

Примітка: MutationObserver насправді не працює у всіх версіях Android 4.4, незважаючи на те, що канеза говорить.
Джеймс Гулд

Дивіться цю подвійку для прикладу зміни кольору фону текстової області на синій, коли ширина текстової області розтягується на 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 включають деякі libs, які я не можу змінювати та викликати подію.


Дуже корисний і розумний
дого

5

Як вважають інші, якщо у вас є контроль над тим, який код змінює стиль елемента, ви можете запустити спеціальну подію, змінивши висоту елемента:

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

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


Це дійсно дуже акуратне рішення, якщо у вас є доступ до коду, який змінює стилі.
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
  }
});

1

Цікаве запитання. Проблема полягає в тому, що висота () не приймає зворотний дзвінок, тому ви не зможете запустити зворотний дзвінок. Використовуйте або animate () або css (), щоб встановити висоту, а потім запустити власну подію у зворотному дзвінку. Ось приклад використання анімаційних (), випробуваних та робіт ( демонстраційних ) як доказів концепції:

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

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

8
Вибачте, але що це довело? Ви trigger()проводите подію вручну. Я думаю, що ОП - це після якоїсь автоматичної події, коли атрибут стилю змінено.
JPot

@JPot він показує, що атрибут висоти не кидає події при зміні.
AnthonyJClink

1

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

Зауважте, що функції jQuery можна змінити і повернути "це", щоб декілька викликів можна було викликати один за одним (наприклад, $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/


-1

У мене була така ж проблема, тому я написав це. Це працює досить добре. Чудово виглядає, якщо ви змішуєте його з деякими переходами CSS.

function toggle_visibility(id) {
   var e = document.getElementById("mjwelcome");
   if(e.style.height == '')
      e.style.height = '0px';
   else
      e.style.height = '';
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.