jQuery에서 CSS 클래스 변경 사항에 대한 이벤트 발생


240

jQuery를 사용하여 CSS 클래스를 추가하거나 변경하면 어떻게 이벤트를 발생시킬 수 있습니까? CSS 클래스를 변경하면 jQuery change()이벤트가 발생합니까?


8
7 년 후, 최신 브라우저에서 MutationObservers가 상당히 광범위하게 채택되면서 여기에 허용 된 답변이 Br 씨로 업데이트되었습니다 .
joelmdev

도움이 될 수 있습니다. stackoverflow.com/questions/2157963/…
PSR

Jason의 답변을 완료 한 후 이것을 발견했습니다 : stackoverflow.com/a/19401707/1579667
Benj

답변:


223

스크립트에서 클래스를 변경할 때마다 a trigger를 사용 하여 자신의 이벤트를 제기 할 수 있습니다 .

$(this).addClass('someClass');
$(mySelector).trigger('cssClassChanged')
....
$(otherSelector).bind('cssClassChanged', data, function(){ do stuff });

그러나 그렇지 않으면 아닙니다. 수업이 바뀔 때 이벤트를 시작하는 구운 방법은 없습니다. change()초점이 입력이 변경된 입력을 떠난 후에 만 ​​발생합니다.

$(function() {
  var button = $('.clickme')
      , box = $('.box')
  ;
  
  button.on('click', function() { 
    box.removeClass('box');
    $(document).trigger('buttonClick');
  });
            
  $(document).on('buttonClick', function() {
    box.text('Clicked!');
  });
});
.box { background-color: red; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div class="box">Hi</div>
<button class="clickme">Click me</button>

jQuery 트리거에 대한 추가 정보


4
이벤트를 트리거 한 다음 함수를 호출하면 어떻게됩니까? 왜 함수를 트리거하는 대신 직접 호출하지 않습니까?
RamboNo5 2009

43
트리거링은 특정 요소가 이벤트를 "구독"할 수있는 메커니즘입니다. 이렇게하면 이벤트가 트리거 될 때 해당 이벤트가 모두 한 번에 발생하여 해당 기능을 실행할 수 있습니다. 예를 들어, 빨간색에서 파란색으로 바뀌는 하나의 객체와 변경을 기다리는 세 객체가있는 경우 변경되면 changeColor이벤트를 트리거 할 수 있으며 해당 이벤트를 구독하는 모든 객체가 그에 따라 반응 할 수 있습니다.
Jason

1
OP가 'cssClassChanged'이벤트를 듣고 싶어하는 것이 조금 이상해 보입니다. 분명히 클래스가 'colourChanged'와 같은 다른 '응용 프로그램'이벤트의 결과로 추가되었습니다.이 클래스는 왜 어떤 것이 특히 className 변경에 관심이 있는지 상상할 수 없기 때문에 트리거로 알리는 것이 더 합리적입니다. classNameChange의 결과는 확실합니까?
riscarrott

그것이 특히 관심있는 className 변경이라면 'cssClassChanged'를 트리거하기 위해 jQuery.fn.addClass를 장식하는 것이 @micred와 같이 더 합리적이라고 상상할 것입니다.
riscarrott

5
@ riscarrott 나는 OP의 응용 프로그램을 알고 있다고 생각하지 않습니다. 나는 단지 그들에게 pub / sub의 개념을 제시하고 있으며 원하는대로 적용 할 수 있습니다. 주어진 정보의 양과 실제 이벤트 이름이 무엇인지 논쟁하는 것은 어리석은 일입니다.
Jason

140

더 나은 해결책은 @ RamboNo5와 @Jason의 두 가지 답변을 결합하는 것입니다.

addClass 함수를 재정의하고라는 사용자 정의 이벤트를 추가하는 것을 의미합니다. cssClassChanged

// Create a closure
(function(){
    // Your base, I'm in it!
    var originalAddClassMethod = jQuery.fn.addClass;

    jQuery.fn.addClass = function(){
        // Execute the original method.
        var result = originalAddClassMethod.apply( this, arguments );

        // trigger a custom event
        jQuery(this).trigger('cssClassChanged');

        // return the original result
        return result;
    }
})();

// document ready function
$(function(){
    $("#YourExampleElementID").bind('cssClassChanged', function(){ 
        //do stuff here
    });
});

이것은 실제로 가장 좋은 방법처럼 들리지만 이벤트를 "#YourExampleElementID"에만 바인딩하더라도 모든 클래스 변경 사항 (예 : 내 페이지의 요소 중 하나)이이 핸들러에 의해 처리되는 것처럼 보입니다.
Filipe Correia

@FilipeCorreia 나에게 잘 작동합니다. 사례 복제와 함께 jsfiddle을 제공 할 수 있습니까?
Arash Milani

@ Goldentoa11 저녁이나 주말에 시간이 걸리고 광고를 많이 사용하는 일반적인 페이지로드에서 발생하는 모든 사항을 모니터링해야합니다. 이와 같은 작업을 시도하는 스크립트 (때로는 가장 화려한 방식으로 실패한 경우)에 의해 날아갈 것입니다. $()실제 작업이 시작될 때 초당 수백 개의 DOMNodeInserted / DOMSubtreeModified 이벤트를 발생시키는 페이지를 가로 질러 총 수백 개의 이벤트가 발생했습니다 .
L0j1k 2018 년

또한 removeClass에서 동일한 이벤트 트리거를 지원하고 싶을 것입니다.
Darius

4
어쨌든 @FilipeCorreia 가이 접근법에 몇 가지 문제가 발생한 이유를 이해합니다.이 cssClassChanged이벤트를 요소에 바인딩하면 자식이 클래스를 변경하더라도 해고 될 것임을 알아야합니다. 따라서 예를 들어 이벤트를 시작하지 않으려면 $("#YourExampleElementID *").on('cssClassChanged', function(e) { e.stopPropagation(); });바인드 후 같은 것을 추가 하십시오. 어쨌든 정말 좋은 해결책입니다. 감사합니다!
tonix

59

클래스 변경을 감지하려면 가장 좋은 방법은 Mutation Observers를 사용하여 모든 속성 변경을 완전히 제어하는 ​​것입니다. 그러나 리스너를 직접 정의하고 듣고있는 요소에 추가해야합니다. 좋은 점은 리스너가 추가되면 수동으로 아무것도 트리거 할 필요가 없다는 것입니다.

$(function() {
(function($) {
    var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;

    $.fn.attrchange = function(callback) {
        if (MutationObserver) {
            var options = {
                subtree: false,
                attributes: true
            };

            var observer = new MutationObserver(function(mutations) {
                mutations.forEach(function(e) {
                    callback.call(e.target, e.attributeName);
                });
            });

            return this.each(function() {
                observer.observe(this, options);
            });

        }
    }
})(jQuery);

//Now you need to append event listener
$('body *').attrchange(function(attrName) {

    if(attrName=='class'){
            alert('class changed');
    }else if(attrName=='id'){
            alert('id changed');
    }else{
        //OTHER ATTR CHANGED
    }

});
});

이 예제에서 이벤트 리스너는 모든 요소에 추가되지만 대부분의 경우 메모리를 저장하지는 않습니다. 이 "attrchange"리스너를 관찰하려는 요소에 추가하십시오.


1
이것은 허용 된 답변과 똑같은 일을하는 것처럼 보이지만 더 많은 코드와 일부 브라우저에서 지원되지 않으며 유연성이 떨어집니다. 이것이 가지고있는 유일한 이점 은 페이지에서 무언가가 변경 될 때마다 실행되어 성능이 완전히 저하된다는 것입니다.
Jason

10
귀하의 진술은 @Json이 잘못되었습니다. 트리거를 수동으로 활성화하지 않고 자동으로 활성화하려는 경우 실제로 수행하는 방법입니다. 그것은 그것이 이익이라는 것뿐만 아니라 이것이 문제라는 질문에 혜택을 얻습니다. 둘째, 이것은 단지 예일 뿐이며, 예에서 언급했듯이 모든 요소에 이벤트 리스너를 추가하는 것이 아니라 듣고 싶은 요소에만 추가하는 것이 좋습니다. 그래서 왜 성능을 파괴하는지 이해하지 못합니다. 그리고 마지막으로 미래입니다. 최신 브라우저는 caniuse.com/#feat=mutationobserver를 지원합니다. 편집을 다시 고려하십시오. 올바른 방법입니다.
Mr Br

1
insertionQ 라이브러리는 당신이 그들이 셀렉터에 일치하는 경우, DOM가 보여주는 노드들이 따라 잡을 수 있습니다. 를 사용하지 않기 때문에 더 넓은 브라우저를 지원합니다 DOMMutationObserver.
Dan Dascalescu

이것은 훌륭한 솔루션입니다. 특히 클래스를 추가하거나 제거 할 때 캔버스 또는 SVG 애니메이션을 시작 및 중지합니다. 필자의 경우 스크롤이 보이지 않을 때 실행되지 않는지 확인했습니다. 단!
BBaysinger

1
2019 년에는 이것이 정답입니다. 모든 주요 브라우저는 현재 Mutation Observers를 지원하는 것으로 보이며이 솔루션은 이벤트를 수동으로 시작하거나 기본 jQuery 기능을 다시 작성할 필요가 없습니다.
sp00n

53

addClass 함수를 재정의하는 것이 좋습니다. 이 방법으로 할 수 있습니다 :

// Create a closure
(function(){
    // Your base, I'm in it!
    var originalAddClassMethod = jQuery.fn.addClass;

    jQuery.fn.addClass = function(){
        // Execute the original method.
        var result = originalAddClassMethod.apply( this, arguments );

        // call your function
        // this gets called everytime you use the addClass method
        myfunction();

        // return the original result
        return result;
    }
})();

// document ready function
$(function(){
    // do stuff
});

16
이것을 Jason의 $ (mySelector) .trigger ( 'cssClassChanged')와 함께 사용하는 것이 최선의 방법이라고 생각합니다.
kosoant 2009

4
일반적으로이를 수행하는 플러그인이 있습니다 : github.com/aheckmann/jquery.hook/blob/master/jquery.hook.js
Jerph

37
미래의 관리자를 혼동시키는 좋은 방법 인 것 같습니다.
roufamatic

1
원래 메소드의 결과를 리턴하는 것을 잊지 마십시오.
Petah

1
참고로 프록시 패턴 en.wikipedia.org/wiki/Proxy_pattern
Darius

22

개념 증명 만 :

요점을보고 주석을보고 최신 상태를 유지하십시오.

https://gist.github.com/yckart/c893d7db0f49b1ea4dfb

(function ($) {
  var methods = ['addClass', 'toggleClass', 'removeClass'];

  $.each(methods, function (index, method) {
    var originalMethod = $.fn[method];

    $.fn[method] = function () {
      var oldClass = this[0].className;
      var result = originalMethod.apply(this, arguments);
      var newClass = this[0].className;

      this.trigger(method, [oldClass, newClass]);

      return result;
    };
  });
}(window.jQuery || window.Zepto));

사용법은 매우 간단합니다. 관찰하려는 노드에 새 목록을 추가하고 일반적으로 클래스를 조작하십시오.

var $node = $('div')

// listen to class-manipulation
.on('addClass toggleClass removeClass', function (e, oldClass, newClass) {
  console.log('Changed from %s to %s due %s', oldClass, newClass, e.type);
})

// make some changes
.addClass('foo')
.removeClass('foo')
.toggleClass('foo');

removeClass 메서드로 이전 또는 새 클래스를 읽을 수 없다는 점을 제외하고는 저에게 효과적이었습니다.
slashdottir

1
@slashdottir 당신은 바이올린을 작성하고 설명 할 수 무엇을 정확하게 일을하지 않습니다 .
yckart

네 ... 작동하지 않습니다. TypeError : this [0]은«return this [0] .className;»에 정의되어 있지 않습니다.
Pedro Ferreira

그것은 (왜?) 가끔 작동하지만 this[0]정의되어 있지 않습니다 ... 단지와 라인의 끝 부분 교체 var oldClassvar newClass다음 과제로를 := (this[0] ? this[0].className : "");
fvlinden

9

최신 jquery 돌연변이 사용

var $target = jQuery(".required-entry");
            var observer = new MutationObserver(function(mutations) {
                mutations.forEach(function(mutation) {
                    if (mutation.attributeName === "class") {
                        var attributeValue = jQuery(mutation.target).prop(mutation.attributeName);
                        if (attributeValue.indexOf("search-class") >= 0){
                            // do what you want
                        }
                    }
                });
            });
            observer.observe($target[0],  {
                attributes: true
            });

// $ target.addClass ( 'search-class')와 같이 $ target에있는 클래스 required-entry 를 갖는 div를 업데이트하는 코드


좋은 해결책, 나는 그것을 사용하고 있습니다. 그러나 나는 react을 사용하고 있으며 문제 중 하나는 웹 요소가 가상으로 생성 될 때마다 관찰자를 추가한다는 것입니다. 관찰자가 이미 있는지 확인하는 방법이 있습니까?
Mikaël Mayer

나는 [링크]에 따르면이 하락되었다고 생각하고 사용하지 않아야합니다 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
WebDude0482

@ WebDude0482 MutationObserver.observe는 "MutationObserver"의 "인스턴스 메소드"이므로 더 이상 사용되지 않습니다. 참조 developer.mozilla.org/en-US/docs/Web/API/MutationObserver
뉴 에베레스트

8

change()CSS 클래스가 추가 또는 제거되거나 정의가 변경 될 때 실행되지 않습니다. 선택 상자 값이 선택되거나 선택되지 않은 상황에서 발생합니다.

CSS 클래스 정의가 변경되었는지 (프로그래밍 방식으로 수행 할 수 있지만 지루하고 일반적으로 권장되지는 않음) 또는 클래스가 요소에 추가되거나 제거되는지 여부를 확실하지 않습니다. 두 경우 모두 이러한 상황을 안정적으로 캡처 할 수있는 방법이 없습니다.

물론 이것에 대한 자신의 이벤트를 만들 수 있지만 이것은 자문으로 만 설명 할 수 있습니다 . 귀하의 코드가 아닌 코드는 캡처하지 않습니다.

또는 addClass()jQuery에서 (etc) 메소드를 재정의 / 교체 할 수 있지만 바닐라 Javascript를 통해 완료되면 캡처되지 않습니다 (이 메소드를 대체 할 수는 있지만).


8

맞춤 이벤트 실행 하지 않고 한 가지 더 방법이 있습니다

Rick Strahl의 HTML 요소 CSS 변경 사항을 모니터링하는 jQuery 플러그인

위에서 인용

watch 플러그인은 FireFox의 DOMAttrModified, Internet Explorer의 onPropertyChanged에 연결하거나 setInterval과 함께 타이머를 사용하여 다른 브라우저의 변경 감지를 처리하여 작동합니다. 불행히도 WebKit은 현재 DOMAttrModified를 일관되게 지원하지 않으므로 Safari와 Chrome은 현재 느린 setInterval 메커니즘을 사용해야합니다.


6

좋은 질문. Bootstrap Dropdown Menu를 사용하고 있으며 Bootstrap Dropdown이 숨겨져있을 때 이벤트를 실행해야했습니다. 드롭 다운이 열리면 클래스 이름이 "button-group"인 포함 div에 "open"클래스가 추가됩니다. 버튼 자체에는 "aria-expanded"속성이 true로 설정되어 있습니다. 드롭 다운이 닫히면 해당 "open"클래스가 포함 된 div에서 제거되고 aria-expanded가 true에서 false로 전환됩니다.

클래스 변경을 감지하는 방법에 대한 질문으로 이어졌습니다.

부트 스트랩을 사용하면 감지 할 수있는 "드롭 다운 이벤트"가 있습니다. 이 링크에서 "드롭 다운 이벤트"를 찾으십시오. http://www.w3schools.com/bootstrap/bootstrap_ref_js_dropdown.asp

다음은 부트 스트랩 드롭 다운에서이 이벤트를 사용하는 빠르고 더러운 예입니다.

$(document).on('hidden.bs.dropdown', function(event) {
    console.log('closed');
});

이제 이것이 일반적인 질문보다 더 구체적이라는 것을 알고 있습니다. 그러나 부트 스트랩 드롭 다운으로 열린 이벤트 또는 닫힌 이벤트를 감지하려는 다른 개발자가 이것이 도움이 될 것이라고 생각합니다. 나처럼, 그들은 처음에 단순히 요소 클래스 변화를 감지하려고 시도하는 경로를 따라갈 수 있습니다 (이것은 그렇게 간단하지는 않습니다). 감사.


5

특정 이벤트를 트리거해야하는 경우 addClass () 메소드를 재정 의하여 'classadded'라는 사용자 정의 이벤트를 발생시킬 수 있습니다.

방법 :

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

$('#myElement').on('classadded', function(ev, newClasses) {
    console.log(newClasses + ' added!');
    console.log(this);
    // Do stuff
    // ...
});

5

DOMSubtreeModified 이벤트를 바인딩 할 수 있습니다. 여기에 예제를 추가하십시오.

HTML

<div id="mutable" style="width:50px;height:50px;">sjdfhksfh<div>
<div>
  <button id="changeClass">Change Class</button>
</div>

자바 스크립트

$(document).ready(function() {
  $('#changeClass').click(function() {
    $('#mutable').addClass("red");
  });

  $('#mutable').bind('DOMSubtreeModified', function(e) {
      alert('class changed');
  });
});

http://jsfiddle.net/hnCxK/13/


0

어떤 이벤트가 클래스를 처음 변경했는지 알면 같은 이벤트에 약간의 지연을 사용하고 클래스를 확인할 수 있습니다. 예

//this is not the code you control
$('input').on('blur', function(){
    $(this).addClass('error');
    $(this).before("<div class='someClass'>Warning Error</div>");
});

//this is your code
$('input').on('blur', function(){
    var el= $(this);
    setTimeout(function(){
        if ($(el).hasClass('error')){ 
            $(el).removeClass('error');
            $(el).prev('.someClass').hide();
        }
    },1000);
});

http://jsfiddle.net/GuDCp/3/


0
var timeout_check_change_class;

function check_change_class( selector )
{
    $(selector).each(function(index, el) {
        var data_old_class = $(el).attr('data-old-class');
        if (typeof data_old_class !== typeof undefined && data_old_class !== false) 
        {

            if( data_old_class != $(el).attr('class') )
            {
                $(el).trigger('change_class');
            }
        }

        $(el).attr('data-old-class', $(el).attr('class') );

    });

    clearTimeout( timeout_check_change_class );
    timeout_check_change_class = setTimeout(check_change_class, 10, selector);
}
check_change_class( '.breakpoint' );


$('.breakpoint').on('change_class', function(event) {
    console.log('haschange');
});
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.