DOM이 렌더링을 마친 후 지시문을 어떻게 실행할 수 있습니까?


115

분명하지 않은 겉보기에 간단한 문제가 있습니다 (Angular JS 문서를 읽음) 해결책 있습니다.

DOM에서 컨테이너의 높이를 정의하기 위해 다른 DOM 요소의 높이를 기반으로 일부 계산을 수행하는 Angular JS 지시문이 있습니다.

이와 비슷한 것이 지시문 내부에서 진행됩니다.

return function(scope, element, attrs) {
    $('.main').height( $('.site-header').height() -  $('.site-footer').height() );
}

문제는 지시문이 실행될 때 $('site-header')찾을 수 없어 필요한 jQuery 래핑 된 DOM 요소 대신 빈 배열을 반환한다는 것입니다.

DOM이로드 된 후에 만 ​​실행되고 일반 jQuery 선택기 스타일 쿼리를 통해 다른 DOM 요소에 액세스 할 수있는 내 지시문 내에서 사용할 수있는 콜백이 있습니까?


1
scope. $ on () 및 scope. $ emit ()을 사용하여 사용자 정의 이벤트를 사용할 수 있습니다. 이것이 옳고 권장되는 접근 방식인지 확실하지 않습니다.
Tosh

답변:


137

$ ( 'site-header') 구성 방법에 따라 다릅니다.

0 지연으로 $ timeout 을 사용할 수 있습니다 . 다음과 같은 것 :

return function(scope, element, attrs) {
    $timeout(function(){
        $('.main').height( $('.site-header').height() -  $('.site-footer').height() );
    });        
}

작동 원리 설명 : 하나 , .

$timeout지시문 에 삽입하는 것을 잊지 마십시오 .

.directive('sticky', function($timeout)

5
고마워, 나는 내가 $timeout지시에 통과하지 못했다는 것을 깨달을 때까지 오랫동안 이것을 작동하도록 노력했습니다 . 도. 이제 모든 것이 작동합니다.
Jannis

5
예, 당신은 통과해야 $timeout다음과 같이 지시어 :.directive('sticky', function($timeout) { return function (scope, element, attrs, controller) { $timeout(function(){ }); }); };
블라디미르 Starkov

19
링크 된 설명은 시간 초과 트릭이 JavaScript에서 작동하지만 AngularJS 컨텍스트에서는 작동하지 않는 이유를 설명합니다. 로부터 공식 문서 : " [...] 4. $ evalAsync 큐는 현재 스택 프레임의 외부에서 발생하는 필요가 일정 작업에 사용되지만, 브라우저의보기 전에 렌더링합니다. 이것은 보통의 setTimeout (0)으로 이루어집니다 만, setTimeout (0) 접근 방식은 속도가 느려지고 브라우저가 각 이벤트 후 뷰를 렌더링하므로 뷰 깜박임이 발생할 수 있습니다. [...] "(강조 내)
Alberto

12
비슷한 문제에 직면하고 있으며 지시문을 실행하기 전에 DOM이로드되도록하려면 약 300ms가 필요하다는 것을 알게되었습니다. 나는 그렇게 겉보기에 임의의 숫자를 연결하는 것을 정말로 좋아하지 않습니다. DOM 로딩 속도는 사용자에 따라 다를 것이라고 확신합니다. 그렇다면 내 앱을 사용하는 모든 사용자에게 300ms가 작동하는지 어떻게 확신 할 수 있습니까?
keepitreal 2014 년

5
이 답변에 너무 만족하지 않습니다 .. OP의 질문에 대답하는 것처럼 보이지만 케이스에 매우 구체적이며 문제의보다 일반적인 형태 (즉, dom이로드 된 후 지시문 실행)와의 관련성이 명확하지 않습니다. + 그것은 단지 너무 해키입니다 .. 각도에 대해 전혀 특별한 것이 없습니다
abbood

44

방법은 다음과 같습니다.

app.directive('example', function() {

    return function(scope, element, attrs) {
        angular.element(document).ready(function() {
                //MANIPULATE THE DOM
        });
    };

});

1
요소가 이미 사용 가능하기 때문에 angular.element도 필요하지 않습니다.element.ready(function(){
timhc22

1
@ timhc22 요소는 지시문을 트리거 한 DOMElement에 대한 참조이며, 권장 사항으로 인해 페이지 문서 객체에 대한 DOMElement 참조가 생성되지 않습니다.
tobius

제대로 작동하지 않습니다. 이 접근 방식을 통해 offsetWidth = 0을 얻고 있습니다
Alexey Sh.

37

아마도 저자는 더 이상 내 대답이 필요하지 않을 것입니다. 그래도 완전성을 위해 다른 사용자가 유용하다고 생각합니다. 가장 좋고 가장 간단한 해결책은 $(window).load()반환 된 함수의 본문 내부에서 사용 하는 것입니다. (또는 사용할 수 있습니다document.ready . 모든 이미지가 필요한지 여부에 따라 다릅니다).

사용 $timeout 내 소견에하는 것은 매우 약한 옵션이며 경우에 따라 실패 할 수 있습니다.

내가 사용할 완전한 코드는 다음과 같습니다.

.directive('directiveExample', function(){
   return {
       restrict: 'A',
       link: function($scope, $elem, attrs){

           $(window).load(function() {
               //...JS here...
           });
       }
   }
});

1
"어떤 경우에는 실패 할 수있는"이유를 설명해 주시겠습니까? 무슨 사건인가요?
rryter

6
여기에서 jQuery를 사용할 수 있다고 가정합니다.
Jonathan Cremin

3
@JonathanCremin jQuery 선택은 OP
Nick Devereaux

1
이것은 훌륭하게 작동하지만 지시문을 사용하여 새 항목을 빌드하는 게시물이있는 경우 초기로드 후에 창로드가 실행되지 않으므로 올바르게 작동하지 않습니다.
Brian Scott

@BrianScott-초기 페이지 렌더링을 위해 $ (window) .load (내 사용 사례가 포함 된 글꼴 파일을 기다리고 있음)를 사용한 다음 element.ready를 사용하여 뷰 전환을 처리했습니다.
AAAAAA

8

ngcontentloaded이벤트 가 있는데 사용할 수있을 것 같아요

.directive('directiveExample', function(){
   return {
       restrict: 'A',
       link: function(scope, elem, attrs){

                $$window = $ $window


                init = function(){
                    contentHeight = elem.outerHeight()
                    //do the things
                }

                $$window.on('ngcontentloaded',init)

       }
   }
});

21
무슨 일인지 설명해 주 $ $window시겠어요?
Catfish

2
커피 스크립트처럼 보입니다. $ ($ window) 및 $ window가 지시문에 삽입되도록
의도되었을 수 있습니다.

5

외부 리소스로 인해 $ timeout을 사용할 수없고 특정 타이밍 문제로 인해 지시문을 사용할 수없는 경우 broadcast를 사용하세요.

$scope.$broadcast("variable_name_here");원하는 외부 리소스 또는 장기 실행 컨트롤러 / 지시문이 완료된 후에 추가하십시오 .

그런 다음 외부 리소스가로드 된 후 아래를 추가합니다.

$scope.$on("variable_name_here", function(){ 
   // DOM manipulation here
   jQuery('selector').height(); 
}

예를 들어 지연된 HTTP 요청의 약속에서.

MyHttpService.then(function(data){
   $scope.MyHttpReturnedImage = data.image;
   $scope.$broadcast("imageLoaded");
});

$scope.$on("imageLoaded", function(){ 
   jQuery('img').height(80).width(80); 
}

2
로드 된 데이터가 DOM 요소에 바인딩 된 적절한 범위 변수에 있더라도 이미 DOM에서 렌더링되었음을 의미하지는 않기 때문에 이것은 문제를 해결하지 못할 것입니다. 스코프에로드되는 순간과 dom에 렌더링 된 출력 사이에는 시간 간격이 있습니다.
René Stalder 2014

1

비슷한 문제가 있었고 여기에서 내 솔루션을 공유하고 싶습니다.

다음 HTML이 있습니다.

<div data-my-directive>
  <div id='sub' ng-include='includedFile.htm'></div>
</div>

문제 : 부모 div 지시문의 링크 기능에서 자식 div # sub를 jquery'ing하고 싶었습니다. 그러나 지시문의 링크 기능이 실행될 때 ng-include가 완료되지 않았기 때문에 빈 객체를주었습니다. 그래서 먼저 $ timeout을 사용하여 더러운 해결 방법을 만들었습니다. 작동했지만 지연 매개 변수는 클라이언트 속도에 따라 달라졌습니다 (아무도 좋아하지 않음).

작동하지만 더럽습니다.

app.directive('myDirective', [function () {
    var directive = {};
    directive.link = function (scope, element, attrs) {
        $timeout(function() {
            //very dirty cause of client-depending varying delay time 
            $('#sub').css(/*whatever*/);
        }, 350);
    };
    return directive;
}]);

다음은 깨끗한 솔루션입니다.

app.directive('myDirective', [function () {
    var directive = {};
    directive.link = function (scope, element, attrs) {
        scope.$on('$includeContentLoaded', function() {
            //just happens in the moment when ng-included finished
            $('#sub').css(/*whatever*/);
        };
    };
    return directive;
}]);

누군가에게 도움이 될 수도 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.