web-dev-qa-db-fra.com

Détecter le changement de hauteur de document

J'essaie de détecter quand ma document hauteur change. Une fois que c'est fait, je dois exécuter quelques fonctions pour aider à organiser ma mise en page.

Je ne cherche pas window.onresize. J'ai besoin de tout le document, qui est plus grand que la fenêtre.

Comment puis-je observer ce changement?

58
Steve Robbins
function onElementHeightChange(Elm, callback){
    var lastHeight = Elm.clientHeight, newHeight;
    (function run(){
        newHeight = Elm.clientHeight;
        if( lastHeight != newHeight )
            callback();
        lastHeight = newHeight;

        if( Elm.onElementHeightChangeTimer )
            clearTimeout(Elm.onElementHeightChangeTimer);

        Elm.onElementHeightChangeTimer = setTimeout(run, 200);
    })();
}


onElementHeightChange(document.body, function(){
    alert('Body height changed');
});

LIVE DEMO

61
vsync

Vous pouvez utiliser un absolute positionné iframe avec une largeur nulle à l'intérieur de l'élément que vous souhaitez surveiller pour les changements de hauteur et écouter les événements resize sur ses contentWindow. Par exemple:

HTML

<body>
  Your content...
  <iframe class="height-change-listener" tabindex="-1"></iframe>
</body>

CSS

body {
  position: relative;
}
.height-change-listener {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  height: 100%;
  width: 0;
  border: 0;
  background-color: transparent;
}

JavaScript (utilisant jQuery mais pouvant être adapté à JS pur)

$('.height-change-listener').each(function() {
  $(this.contentWindow).resize(function() {
    // Do something more useful
    console.log('doc height is ' + $(document).height());
  });
});

Si pour une raison quelconque vous avez height:100% mis sur body vous aurez besoin de trouver (ou d’ajouter) un autre élément conteneur pour l’implémenter Si vous voulez ajouter le iframe de manière dynamique, vous devrez probablement utiliser le <iframe>.load événement pour attacher le contentWindow.resize auditeur. Si vous voulez que cela fonctionne dans IE7 ainsi que dans les navigateurs, vous devrez ajouter le *zoom:1 hackez l'élément conteneur et écoutez également l'événement 'propriétaire' resize sur le <iframe> élément lui-même (qui dupliquera contentWindow.resize dans IE8-10).

Voici un violon ...

23
Jake

Juste mes deux cents. Si par hasard vous utilisez angular, alors cela ferait l'affaire:

$scope.$watch(function(){ 
 return document.height();
},function onHeightChange(newValue, oldValue){
 ...
});
12
Dan Ochiana

Comme mentionné par vsync, il n'y a pas d'événement mais vous pouvez utiliser une minuterie ou attacher le gestionnaire ailleurs:

// get the height
var refreshDocHeight = function(){
    var h = $(document).height();
    $('#result').html("Document height: " + h);
};

// update the height every 200ms
window.setInterval(refreshDocHeight, 200);

// or attach the handler to all events which are able to change 
// the document height, for example
$('div').keyup(refreshDocHeight);

Trouvez le jsfiddle ici .

4
Marc

la réponse de vsync est tout à fait correcte. Juste au cas où vous n'aimeriez pas utiliser setTimeout et que vous puissiez utiliser requestAnimationFrame ( voir support ) et bien sûr que vous êtes toujours intéressé.

Dans l'exemple ci-dessous, le corps obtient un événement supplémentaire sizechange. Et chaque fois que la hauteur ou la largeur du corps change, elle est déclenchée.

(function checkForBodySizeChange() {
    var last_body_size = {
        width: document.body.clientWidth,
        height: document.body.clientHeight
    };

    function checkBodySizeChange()
    {
        var width_changed = last_body_size.width !== document.body.clientWidth,
            height_changed = last_body_size.height !== document.body.clientHeight;


        if(width_changed || height_changed) {
            trigger(document.body, 'sizechange');
            last_body_size = {
                width: document.body.clientWidth,
                height: document.body.clientHeight
            };
        }

        window.requestAnimationFrame(checkBodySizeChange);
    }

    function trigger(element, event_name, event_detail)
    {
        var evt;

        if(document.dispatchEvent) {
            if(typeof CustomEvent === 'undefined') {
                var CustomEvent;

                CustomEvent = function(event, params) {
                    var evt;
                    params = params || {
                        bubbles: false,
                        cancelable: false,
                        detail: undefined
                    };
                    evt = document.createEvent("CustomEvent");
                    evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
                    return evt;
                };

                CustomEvent.prototype = window.Event.prototype;

                window.CustomEvent = CustomEvent;
            }

            evt = new CustomEvent(event_name, {"detail": event_detail});

            element.dispatchEvent(evt);
        }
        else {
            evt = document.createEventObject();
            evt.eventType = event_name;
            evt.eventName = event_name;
            element.fireEvent('on' + event_name, evt);
        }
    }

    window.requestAnimationFrame(checkBodySizeChange);
})();

ne démo en direct

Le code peut être beaucoup réduit si vous avez une propre fonction triggerEvent dans votre projet. Il suffit donc de supprimer la fonction complète trigger et de remplacer la ligne trigger(document.body, 'sizechange'); par, par exemple, dans jQuery $(document.body).trigger('sizechange');.

2
Frank Eisenhardt

J'utilise la solution de @ vsync, comme ceci. Je l'utilise pour le défilement automatique sur une page telle que Twitter.

const scrollInterval = (timeInterval, retry, cb) => {
    let tmpHeight = 0;
    const myInterval = setInterval(() => {
        console.log('interval');
        if (retry++ > 3) {
            clearInterval(this);
        }
        const change = document.body.clientHeight - tmpHeight;
        tmpHeight = document.body.clientHeight;
        if (change > 0) {
            cb(change, (retry * timeInterval));
            scrollBy(0, 10000);
        }
        retry = 0;
    }, timeInterval);
    return myInterval;
};

const onBodyChange = (change, timeout) => {
    console.log(`document.body.clientHeight, changed: ${change}, after: ${timeout}`);
}

const createdInterval = scrollInterval(500, 3, onBodyChange);

// stop the scroller on some event
setTimeout(() => {
    clearInterval(createdInterval);
}, 10000);

Vous pouvez également ajouter un minimum de changement, et beaucoup d'autres choses ... Mais cela fonctionne pour moi

0
Johan Hoeksma