ฉันจะเรียกใช้คำสั่งหลังจากที่ Dom เสร็จสิ้นการแสดงผลได้อย่างไร


115

ฉันมีปัญหาที่ดูเหมือนง่ายและไม่ชัดเจน(โดยการอ่านโซลูชันAngular JS docs)

ฉันมีคำสั่ง Angular JS ที่ทำการคำนวณบางอย่างตามความสูงขององค์ประกอบ DOM อื่น ๆ เพื่อกำหนดความสูงของคอนเทนเนอร์ใน DOM

สิ่งที่คล้ายกับสิ่งนี้เกิดขึ้นภายในคำสั่ง:

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

ปัญหาคือเมื่อคำสั่งทำงาน$('site-header')ไม่พบส่งคืนอาร์เรย์ว่างแทนที่จะเป็นองค์ประกอบ DOM ที่ห่อหุ้ม jQuery ที่ฉันต้องการ

มีการเรียกกลับที่ฉันสามารถใช้ภายในคำสั่งของฉันซึ่งทำงานหลังจากโหลด DOM แล้วเท่านั้นและฉันสามารถเข้าถึงองค์ประกอบ DOM อื่น ๆ ผ่านแบบสอบถามสไตล์ตัวเลือก jQuery ปกติได้หรือไม่


1
คุณสามารถใช้ขอบเขต $ on () และขอบเขต $ emit () เพื่อใช้เหตุการณ์ที่กำหนดเอง ไม่แน่ใจว่านี่เป็นแนวทางที่ถูกต้อง / แนะนำหรือไม่

คำตอบ:


137

ขึ้นอยู่กับวิธีสร้าง $ ('site-header') ของคุณ

คุณสามารถลองใช้$ timeoutโดยมีการหน่วงเวลา 0 สิ่งที่ต้องการ:

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

คำอธิบายวิธีการทำงาน: หนึ่ง , สองสอง

อย่าลืมฉีดยา$timeoutในคำสั่งของคุณ:

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

5
ขอบคุณฉันพยายามทำให้สิ่งนี้ใช้ได้ผลมานานหลายปีจนกระทั่งฉันรู้ว่าฉันไม่ได้ผ่าน$timeoutเข้าสู่คำสั่ง Doh ทุกอย่างทำงานตอนนี้ไชโย
Jannis

5
ใช่คุณต้องส่ง$timeoutคำสั่งเช่นนี้:.directive('sticky', function($timeout) { return function (scope, element, attrs, controller) { $timeout(function(){ }); }); };
Vladimir Starkov

19
คำอธิบายที่เชื่อมโยงของคุณอธิบายว่าเหตุใดเคล็ดลับการหมดเวลาจึงทำงานใน JavaScript แต่ไม่ใช่ในบริบทของ AngularJS จากเอกสารอย่างเป็นทางการ : " [... ] 4. คิว $ evalAsync ใช้เพื่อกำหนดเวลาการทำงานที่จำเป็นต้องเกิดขึ้นนอกสแต็กเฟรมปัจจุบัน แต่ก่อนการเรนเดอร์มุมมองของเบราว์เซอร์มักจะทำด้วย setTimeout (0)แต่ วิธี setTimeout (0) ทนทุกข์ทรมานจากความช้าและอาจทำให้มุมมองกะพริบเนื่องจากเบราว์เซอร์แสดงมุมมองหลังจากแต่ละเหตุการณ์ [... ] "(เน้นของฉัน)
Alberto

12
ฉันประสบปัญหาที่คล้ายกันและพบว่าฉันต้องใช้เวลาประมาณ 300 มิลลิวินาทีเพื่อให้ DOM โหลดก่อนที่จะดำเนินการตามคำสั่งของฉัน ฉันไม่ชอบการเสียบตัวเลขที่ดูเหมือนจะเป็นไปตามอำเภอใจแบบนั้นจริงๆ ฉันแน่ใจว่าความเร็วในการโหลด DOM จะแตกต่างกันไปขึ้นอยู่กับผู้ใช้ แล้วฉันจะแน่ใจได้อย่างไรว่า 300ms จะใช้ได้กับทุกคนที่ใช้แอพของฉัน
keepitreal

5
ไม่ค่อยพอใจกับคำตอบนี้ .. ในขณะที่ดูเหมือนว่าจะตอบคำถามของ OP .. มันเจาะจงมากสำหรับกรณีของพวกเขาและมันเกี่ยวข้องกับรูปแบบทั่วไปของปัญหา (เช่นการเรียกใช้คำสั่งหลังจากโหลด dom แล้ว) ไม่ชัดเจน + มัน
แฮ็

44

นี่คือวิธีการ:

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

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

});

1
ไม่ควรจำเป็นต้องใช้เชิงมุมองค์ประกอบเนื่องจากมีองค์ประกอบอยู่แล้วที่นั่น: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. พร้อมที่จะดูแลการสลับมุมมอง
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กำลังทำอะไรอยู่?
ปลาดุก

2
ดูเหมือนกาแฟบางอย่างอาจจะหมายถึง $ ($ window) และ $ window ที่ถูกฉีดเข้าไปในคำสั่ง
mdob

5

หากคุณไม่สามารถใช้ $ timeout เนื่องจากทรัพยากรภายนอกและไม่สามารถใช้คำสั่งได้เนื่องจากปัญหาเฉพาะเกี่ยวกับเวลาให้ใช้การออกอากาศ

เพิ่ม$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 ก็ตาม มีช่วงเวลาระหว่างช่วงเวลาที่โหลดในขอบเขตและเอาต์พุตที่แสดงผลในโดม
René Stalder

1

ฉันมีปัญหาที่คล้ายกันและต้องการแบ่งปันวิธีแก้ปัญหาของฉันที่นี่

ฉันมี HTML ต่อไปนี้:

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

ปัญหา: ในฟังก์ชั่นการเชื่อมโยงของคำสั่งของ div ผู้ปกครองฉันต้องการ jquery'ing ลูก div # ย่อย แต่มันทำให้ฉันเป็นวัตถุว่างเปล่าเพราะ 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.