Có thể nghe một sự kiện thay đổi phong cách của người Viking không?


206

Có thể tạo một trình lắng nghe sự kiện trong jQuery có thể bị ràng buộc với bất kỳ thay đổi kiểu nào không? Ví dụ: nếu tôi muốn "làm" một cái gì đó khi một phần tử thay đổi kích thước hoặc bất kỳ thay đổi nào khác trong thuộc tính kiểu tôi có thể làm:

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

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

Nó sẽ thực sự hữu ích.

Có ý kiến ​​gì không?

CẬP NHẬT

Xin lỗi vì đã tự trả lời, nhưng tôi đã viết một giải pháp gọn gàng có thể phù hợp với người khác:

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

Điều này sẽ tạm thời ghi đè phương thức mẫu.css bên trong và xác định lại nó bằng một kích hoạt ở cuối. Vì vậy, nó hoạt động như thế này:

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

$('p').width(100);
$('p').css('color','red');

yup, giải pháp tốt, nhưng trong một ứng dụng thực tế, tôi nghi ngờ việc này có thể chiếm nhiều tài nguyên và thậm chí làm cho trình duyệt có vẻ chậm theo kịp các tương tác của người dùng. Tôi muốn biết đó có phải là trường hợp hay không.
pixeline

1
@pixeline: Tôi không thể thấy rằng nó sẽ chậm hơn nhiều. Dù sao thì jQuery vẫn gọi nguyên mẫu css, tôi chỉ cần thêm một kích hoạt cho nó. Vì vậy, về cơ bản nó chỉ là một cuộc gọi phương thức bổ sung.
David Hellsing

Tôi hiểu rằng: dường như trong dòng người dùng tương tác với một ứng dụng, css () được gọi là rất nhiều thời gian. Tùy thuộc vào những gì bạn làm với trình kích hoạt đó (nếu đó chỉ là nhật ký bảng điều khiển, tôi đoán bạn tất nhiên an toàn), điều này có thể tạo ra sự cố. Tôi chỉ đang tự hỏi. Nó phụ thuộc vào ứng dụng quá. Cá nhân tôi có xu hướng làm rất nhiều phản hồi trực quan trong công cụ của tôi.
pixeline

Có thêm một vài điều cần làm để làm cho nó hoạt động với các lệnh css jquery hiện có: 1) bạn muốn trả phần tử ra khỏi chức năng css mới của mình nếu không nó sẽ phá vỡ các cuộc gọi css kiểu trôi chảy. ví dụ. $ ('p'). css ('hiển thị', 'khối'). css ('màu', 'đen'); v.v. 2) nếu đó là lệnh gọi giá trị thuộc tính (ví dụ: 'obj.css (' height ');') bạn sẽ muốn trả về giá trị trả về của hàm ban đầu - Tôi thực hiện điều này bằng cách kiểm tra xem danh sách đối số có hay không cho hàm chỉ chứa một đối số.
theringostarrs

1
Tôi đã tạo một phiên bản mạnh mẽ hơn của phương pháp này cũng hoạt động khi chính thuộc tính style đang bị xóa. Nó sẽ đưa ra một sự kiện "phong cách" cho mỗi phong cách đang bị loại bỏ theo cách này. gist.github.com/bbottema/426810c21ae6174148d4
Benny Bottema

Câu trả lời:


19

Vì jQuery là mã nguồn mở, tôi đoán rằng bạn có thể điều chỉnh csshàm để gọi một hàm bạn chọn mỗi khi nó được gọi (truyền đối tượng jQuery). Tất nhiên, bạn sẽ muốn quét mã jQuery để đảm bảo không có gì khác mà nó sử dụng bên trong để đặt các thuộc tính CSS. Lý tưởng nhất là bạn muốn viết một plugin riêng cho jQuery để nó không can thiệp vào chính thư viện jQuery, nhưng bạn sẽ phải quyết định liệu điều đó có khả thi cho dự án của bạn hay không.


1
Nhưng trong trường hợp này điều gì sẽ xảy ra nếu tôi đặt thuộc tính kiểu thẳng với các hàm DOM ??
Jaime Hablutzel

Dưới đây là một ví dụ đầy đủ về giải pháp này: gist.github.com/bbottema/426810c21ae6174148d4
Benny Bottema

272

Mọi thứ đã thay đổi một chút kể từ khi câu hỏi được đặt ra - giờ đây có thể sử dụng M mutObserver để phát hiện các thay đổi trong thuộc tính 'style' của một thành phần, không yêu cầu 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'] });

Đối số được truyền cho hàm gọi lại là một đối tượng M mutRecord cho phép bạn nắm giữ các giá trị kiểu cũ và mới.

Hỗ trợ tốt trong các trình duyệt hiện đại bao gồm IE 11+.


19
Hãy nhớ rằng điều này chỉ hoạt động với các kiểu nội tuyến, không phải khi kiểu thay đổi do hậu quả của việc thay đổi hoặc thay đổi lớp @media.
Fregante

2
@ bfred.it bạn có ý tưởng làm thế nào để đạt được điều đó không? Tôi muốn chạy một số js sau khi các quy tắc css của lớp được áp dụng cho một phần tử.
Kragalon

Bạn có thể sử dụng getComputedStylevới setIntervalnhưng điều đó sẽ làm chậm trang web của bạn rất nhiều.
fregante

Lưu ý: M mutObserver không thực sự hoạt động trong tất cả các phiên bản Android 4.4, mặc dù những gì caniuse nói.
James Gould

Xem twiddle này để biết ví dụ về việc thay đổi màu nền của textarea thành màu xanh khi chiều rộng của textarea được kéo dài quá 300px. jsfiddle.net/RyanNerd/y2q8wuf0 (thật không may, có một lỗi trong FF treo kịch bản tạiHTMLElement.style.prop = "value"
RyanNerd

18

Việc khai báo đối tượng sự kiện của bạn phải nằm trong hàm css mới của bạn. Nếu không, sự kiện chỉ có thể được sa thải một lần.

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

Tuyệt vời cảm ơn bạn. Một số người nói thay đổi nguồn ban đầu nhưng phần mở rộng của bạn là tốt nhất. Tôi đã có một số vấn đề nhưng cuối cùng nó hoạt động.
ccsakuweb

Nó liên tục bắn, sau khi thực thi mã này, và thậm chí không đạt được kết quả yêu cầu.
smkrn110

13

Tôi nghĩ rằng câu trả lời tốt nhất nếu từ Mike trong trường hợp bạn không thể khởi chạy sự kiện của mình vì không phải từ mã của bạn. Nhưng tôi nhận được một số lỗi khi tôi sử dụng nó. Vì vậy, tôi viết một câu trả lời mới để cho bạn thấy mã mà tôi sử dụng.

Sự mở rộng

// 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;
    }
})();

Sử dụng

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

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

Tôi đã gặp lỗi vì var ev = new $ .Event ('style'); Một cái gì đó giống như phong cách không được xác định trong HtmlDiv .. Tôi đã xóa nó và bây giờ tôi khởi chạy $ (this) .trigger ("stylechanged"). Một vấn đề khác là Mike đã không trả lại kết quả là $ (css, ..) sau đó Nó có thể gây ra vấn đề trong một số trường hợp. Vì vậy, tôi nhận được kết quả và trả lại nó. Bây giờ hoạt động ^^ Trong mọi thay đổi css bao gồm từ một số lib mà tôi không thể sửa đổi và kích hoạt một sự kiện.


Rất hữu ích và thông minh
dgo

5

Như những người khác đã đề xuất, nếu bạn có quyền kiểm soát bất kỳ mã nào đang thay đổi kiểu của phần tử, bạn có thể kích hoạt một sự kiện tùy chỉnh khi bạn thay đổi chiều cao của phần tử:

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

Mặt khác, mặc dù khá tốn tài nguyên, bạn có thể đặt bộ hẹn giờ để kiểm tra định kỳ các thay đổi về chiều cao của phần tử ...


Đây thực sự là một giải pháp rất gọn gàng nếu một người có quyền truy cập vào mã đang thay đổi kiểu.
Umair Hafeez

2

Không có hỗ trợ sẵn có cho sự kiện thay đổi kiểu trong jQuery hoặc trong tập lệnh java. Nhưng jQuery hỗ trợ để tạo sự kiện tùy chỉnh và lắng nghe nó nhưng mỗi khi có thay đổi, bạn nên có cách tự kích hoạt nó. Vì vậy, nó sẽ không phải là một giải pháp hoàn chỉnh.


2

bạn có thể dùng thử Jquery, nó kích hoạt các sự kiện khi css thay đổi và dễ sử dụng

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

Câu hỏi thú vị. Vấn đề là chiều cao () không chấp nhận cuộc gọi lại, vì vậy bạn sẽ không thể thực hiện cuộc gọi lại. Sử dụng animate () hoặc css () để đặt chiều cao và sau đó kích hoạt sự kiện tùy chỉnh trong cuộc gọi lại. Dưới đây là một ví dụ sử dụng animate (), đã thử nghiệm và hoạt động ( bản demo ), như một bằng chứng về khái niệm:

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

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

8
Xin lỗi, nhưng điều này đã chứng minh điều gì? Bạn trigger()sự kiện bằng tay. Tôi sẽ nghĩ rằng OP là sau một số sự kiện tự động kích hoạt khi một thuộc tính phong cách được sửa đổi.
JPot

@JPot anh ấy đang cho thấy rằng thuộc tính chiều cao không ném các sự kiện khi thay đổi.
AnthonyJClink

1

Chỉ cần thêm và chính thức hóa giải pháp của @David từ phía trên:

Lưu ý rằng các hàm jQuery có thể kết nối được và trả về 'cái này' để có thể gọi nhiều lệnh khác nhau (ví dụ: $container.css("overflow", "hidden").css("outline", 0); ).

Vì vậy, mã được cải thiện nên là:

(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

Làm thế nào về jQuery cssHooks?

Có thể tôi không hiểu câu hỏi, nhưng những gì bạn đang tìm kiếm dễ dàng thực hiện cssHooksmà không thay đổicss() chức năng.

sao chép từ tài liệu:

(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

Tôi đã có cùng một vấn đề, vì vậy tôi đã viết này. Nó hoạt động khá tốt. Trông thật tuyệt nếu bạn kết hợp nó với một số chuyển tiếp CSS.

function toggle_visibility(id) {
   var e = document.getElementById("mjwelcome");
   if(e.style.height == '')
      e.style.height = '0px';
   else
      e.style.height = '';
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.