内容完全加载后测量元素的高度

And*_*ark 5 javascript jquery

我对JavaScript/jQuery /编程一般都是新手,所以如果我遇到痛苦的天真,请耐心等待.

我编写了一个脚本,将Tumblr博客上的脚注转换为"旁注".在大多数情况下,它的效果非常好.你可以在我的博客上看到例子.

我这样做是通过将脚注的innerHTML附加到空div,然后用CSS(display: none)隐藏原始脚注.我根本不想改变帖子的标记.我的想法是我可以删除脚本和旁注还原或"降级"回脚注.

侧注相对于其容器是绝对定位的,它们的位置取决于几个因素:前一个侧边的位置,文本中参考链接的位置以及容器的位置.理想情况下,旁注应出现在与文本中的参考链接相同的垂直偏移处,但如果前一个元素在路上,则向下移动以避免重叠.

脚本非常简单.基本算术.为了避免重叠元素,我计算从页面顶部到前一个元素底部的偏移量.如果它大于参考链接的偏移量(以及容器的偏移量),则元素向下移动.

如您所见,如果脚注仅包含文本,则一切都很完美.但如果它们也包含图像,则旁注开始重叠.我相信这是因为我使用.outerHeight()来获取元素的高度,但是如果图像尚未加载,则返回错误的值.您可以在我的某个测试页面上看到我正在谈论的内容示例.

我想我已经将问题分离到了下面的函数中.

我想如果我使用.load,它会在执行代码之前等待元素的内容加载.但这似乎并没有发生.

请告诉我我可能做错了什么.我很确定脚本的问题包含在下面的代码中,但如果您认为这看起来很好,我也可以发布其余的脚本.或者你可以在http://resources.contentioninvain.com/scripts/FootnotesToSidenotes.js(对不起,我是一个新用户,所以我不能发布超过两个链接,哈哈).

在此先感谢您的任何建议.这是我第一次发布问题,但我发现这个网站过去非常有用.除了修补之外,这是我第一次尝试从头开始编写脚本(我很自豪我没有帮助就得到了这么多,哈哈).

// GetBottomOffset gets the vertical offset to the bottom of an element
// It adds the element's vertical offset and its height:
function GetBottomOffset(Element) {
    $(Element).load(function() {
        var ElementTop = Element.offset().top; // Vert offset of element
        var ElementHeight = Element.outerHeight(true); // Height of element

        // Offset to bottom of element
        var ElementBottom = ElementTop + ElementHeight;

        // Returns ElementBottom
        return ElementBottom;
    }); 
}
Run Code Online (Sandbox Code Playgroud)

T.J*_*der 5

我认为存在三个不相关的问题:

  1. 我认为您还必须单独观看要加载的图像,并且仅在加载所有内容时才进行计算。
  2. 如果您先等待事件发生,则无法从函数返回值,因为该函数在事件发生之前已经返回。您需要使用回调。
  3. 在挂钩事件之前,您必须允许加载图像load

所以例如:

function watchForBottomDimension(element, callback) {
    var $element, images, loaded;

    // Get a jQuery wrapper for `element`
    $element = $(element);

    // Find the images within it that aren't loaded yet
    images = $element.find("img").filter(function(index, img) {
        return !img.complete;
    });

    // Find any?
    if (images.length === 0) {
        // No, we're done -- call the callback asynchronously for
        // consistency with the case below where we have to wait
        setTimeout(done, 0);
    }
    else {
        // We're waiting for some images, do that
        loaded = 0;
        images.load(function() {
            // One of them loaded; was it the last one?
            if (++loaded === images.length) {
                // Yup, we're done
                done();
            }
        }); 
    }

    // Handle completion
    function done() {
        var top, height, bottom;

        top = $element.offset().top; // Vert offset of element
        height = $element.outerHeight(true); // Height of element

        // Offset to bottom of element
        bottom = top + height;

        // Call the callback with the value
        callback(element, bottom);
    }
}
Run Code Online (Sandbox Code Playgroud)

一些注意事项:

  1. 这是题外话,但它可能是你在上面注意到的第一件事,所以:我已经改变了事物的名称。在 JavaScript 中,压倒性的约定是对除构造函数(您主要使用new,如调用的函数)之外的所有内容使用首字母小写字母Date,它们最初是有上限的,以及有时以全部大写形式完成的伪常量。所以element而不是ElementdoSomething而不是DoSomething,等等。
  2. 我正在检查图像是否通过HTMLImageElement#complete. 但是,根据该规范,存在竞争条件,因为completed在我们检查它和我们挂钩load事件之间可能会发生变化(直到我去寻找参考时我才意识到这一点;令人惊讶),所以更强大的方法可能是必需的。
  3. 您的原始代码的一部分假设Element是一个 jQuery 实例,其他部分则将它传入$,好像它不是。在上面我假设它是一个原始的 DOM 元素;如果它是一个jQuery的实例,而不是,只是删除$element = $(element)位和使用element代替$element
  4. 请注意, mywatchForBottomDimension接受一个名为 的参数callback,它应该是一个函数。稍后,它使用维度调用该函数(也传回元素,必须供参考)。
  5. 我删除了观察元素本身的部分;它不应该是必要的(除非元素是图像)。但我添加了观察其中的图像 - 特别是(尚未)的图像loaded
  6. 有两种终止条件:A)没有我们需要等待的图像,B)有。所以我们需要done在两个地方做我们的逻辑,所以我把它放在一个嵌套函数中(因为这些地方之一在load事件处理程序中)。
  7. 由于我们的终止条件之一是asynchronous,我使它们异步,因此调用者看到相同的东西(异步结果),无论是否有图像要观看,done通过setTimeout在我们不需要的情况下调用via等什么。
  8. 以上不注意图像加载错误(通过error),它可能应该;如果图像加载失败,它将永远不会发出回调。

您可能会发现这个关于观察图像加载的其他 SO 答案很有帮助。