将元素定位在另一个

时间:2015-11-25 12:12:11

标签: javascript jquery

在长文本文档中,有一些“特殊单词”,我想在左边显示注释/注释。每个音符应该尽可能接近到它所引用的单词的级别。

用于此的HTML以表格形式组织。每个段落都是一个表格行,包括左侧的注释和右侧表格列中的主要文本。注释/注释向左移动。但是,不幸的是,还有其他一些元素/文本节点。

<table>
    <tr>
        <td class"comments">
            <span id="dog" class="note">Note for dog</span>
            <span id="cat" class="note">Note for cat</span>
            <span id="horse" class="note">Note for horse</span>
            Somethin else than a note.
        </td>
        <td>[Text...]
            <span id="dog_anchor" class="reference">Dog</span>
            <span id="cat_anchor" class="reference">Cat</span>
            <span id="horse_anchor" class="reference">Horse</span>
            [Text...]
        </td>
    </tr>
</table>

很容易将“注释” - span更改为absolute并将其定位在参考级别上:

$('span[class*="note"]').each(function (index, value) {
    var my_id = $(this).attr('id');
    var element_ref = document.getElementById(my_id + "_anchor"); // get reference element
    var pos_of_ref = element_ref.offsetTop; // get position of reference element
    $(this).css('top', pos_of_ref); // set own position to position of reference element
});

然而,这里的生活并非如此简单。由于在一行中可能有很多引用词(而在其他行中没有它们)我需要一种相当复杂的方式来分发音符,以便它们尽可能接近它们的引用而不会破坏布局中的任何内容(例如,放置在表格单元格之外或与其他元素重叠。)

此外,表格单元格的高度无法更改。不得移动不是注释的元素。 (注意元素总是按它们在正文中出现的顺序排列。这不是问题。) 所以,我需要一个像这样的算法:

  • 在表格单元格中记下所有笔记。
  • 分析该表格单元格中的空白区域:哪些区域为空白,哪些区域被阻止?
  • 在表格单元格中分发注释,使每个注释尽可能接近其参考字,而不会有任何元素与表格单元格中的任何其他项目发生碰撞。

有没有快速而优雅的方法可以做到这一点,而无需编写数百行代码?

这是一个JSfiddle:https://jsfiddle.net/5vLsrLa7/7/

[建议解决方案更新]

只需将侧边音符的位置设置为relative或仅向下移动音符就不起作用,因为在这种情况下,侧边音符将相对于其所需位置向下移动,从而产生侧边音符方式远离他们的参考词。毕竟,为了获得一个简洁的解决方案,我需要在两个方向上传播注释:向上和向下。

[更新] 预期的结果将是这样的: Expected result: note/reference placing

如您所见,永远不可能将所有音符放在参考的高度。但是,可用空间用于尽可能靠近它们,将它们上下移动。

3 个答案:

答案 0 :(得分:1)

我更改了move()功能如下:

function move(){
    var prev_offset = 0;
    $('span.note').each(function (index, value){ 
        var my_id = $(this).attr('id');
        var element_ref = document.getElementById(my_id + "_anchor"); // get reference element
        var pos_of_ref = element_ref.offsetTop; // get position of reference element

        if (prev_offset >= pos_of_ref){
            pos_of_ref = prev_offset + 30;
        }
        $(this).css('top', pos_of_ref); // set own position to position of reference element
        prev_offset = pos_of_ref;
    });
}

答案 1 :(得分:1)

我假设你的元素的音符总是以正确的顺序

我对你的javascript进行了一些更改:

function move()
{
    var arrayTops = [];
    $('span[class*="note"]').each(function (index, value)
    { 
        var my_id = $(this).attr('id');
        var element_ref = document.getElementById(my_id + "_anchor"); // get reference element
        var pos_of_ref = element_ref.offsetTop; // get position of reference element
        pos_of_ref = getCorrectTopPosition(arrayTops,pos_of_ref);
        $(this).css('top', pos_of_ref); // set own position to position of reference element
        arrayTops.push(pos_of_ref);
    });
}

function getCorrectTopPosition(arrayTops, newOffsetTop)
{
    var notesHeight = 18;
    var marginBetweenNotes = 3;
    var noteheightWithMargin = notesHeight + marginBetweenNotes;  
    var lastTop = arrayTops[arrayTops.length-1];
    if((lastTop + noteheightWithMargin) >= newOffsetTop)
        return lastTop + noteheightWithMargin;
    return newOffsetTop;    
}

答案 2 :(得分:0)

感谢所有的答案和评论。我终于能够弄清楚至少一个对我有用的局部解决方案。

首先,我能够重构我的HTML,所以现在是#34; non note&#34;左边td中的元素都包含在一个div中,这是td中的第一个元素。所以,现在笔记之间没有任何内容,也许在它们之前就有了什么。

我的解决方案的想法不是给笔记一个新的位置,而是为每个笔记设置一个新的margin-top。在表格单元格中添加的margin-top值的最大数量在之前计算(称为&#34;漫游空间&#34;),即表格单元格中最后一个音符下方的空格。因此,表布局不会被破坏。

function move_notes() {
    $('tr').each(function (index, value) {
        var current_tr = $(this);
        var last_app_element_in_tr = $(this).find('span[class*="note"]').last();
        if ($(last_app_element_in_tr).length) /* Only preceed if there is at least one note in the table row */ {
            var tr_height = $(this).height();
            var tr_offset = $(this).offset().top;
            var bottom_of_tr = tr_offset + tr_height;
            var bottom_of_last_app_el = $(last_app_element_in_tr).offset().top + $(last_app_element_in_tr).height();
            var roaming_space = bottom_of_tr - bottom_of_last_app_el; // Calculate the amount of pixels which are "free": The space below the very last note element

            $(this).find('span[class*="note"]').each(function (index, value) {
                var my_id = $(this).attr('id');
                var element_ref = $(current_tr).find("#" + my_id + "_anchor");
                var pos_of_ref = $(element_ref).offset().top;
                var new_margin_top;

                /* Calculate the new margin top: The note should be at the same level as the reference element.
                When loading, in most cases the notes are placed too high. So, the margin top of the note should equal
                the amount of pixels which the note is "too high". So we subtract the height and the offset of the element
                before the current note from the offset of the reference. */
                var previous_note = $(this).prev();
                // not just notes, but every element in the td in general
                if (! $(previous_note).length) // If there is no previous_note, than take the table cell
                {
                    closest_td = $(this).closest("td");
                    new_margin_top = pos_of_ref - $(closest_td).offset().top;
                } else {
                    new_margin_top = pos_of_ref - $(previous_note).offset().top - $(previous_note).height();
                }

                var difference_to_previous = $(this).css('marginTop').replace(/[^-\d\.]/g, '') - new_margin_top; // Calculate the difference between the old and the new margin top

                if (new_margin_top > 0 && Math.abs(difference_to_previous) > 2) // Only move, if the new margin is greater than zero (no negative margins!) if the difference is greater than 2px (thus preventing ugly "micro moving".
                {
                    var new_roaming_space = roaming_space - difference_to_previous;
                    if (new_roaming_space > 0) /* if there is still room to move */ {
                        var margin_top_ready = new_margin_top + "px";
                        $(this).css('margin-top', margin_top_ready);
                        roaming_space = new_roaming_space;
                    } else /* If there is no more space to move: */ {
                        var margin_top_ready = roaming_space + "px"; // take the rest of the "roaming space" left as margin top
                        $(this).css('margin-top', margin_top_ready);
                        return false; // Stop the execution because there is nothing left to do.
                    }
                }
            });
        }
    });
}

window.onload = function () {
    move_notes();
};

$(window).resize(function () {
    move_notes();
});

正如您将注意到的那样,我的一个主要问题仍然没有得到解决:笔记只会向下移动,而不会向上移动。由于我的真实世界网页存在各种问题,我还没有实现。但是,算法可能是这样的:如果新的边距顶部大于当前音符的高度,并且当前音符锚点和下一个音符锚点的offet之间的差异小于当前音符的高度,从新边距中减去当前音符的高度。

仍然存在两个问题:

  • 如果窗口最大化或快速从相当薄的宽度调整到更大的宽度,则音符位置的调整将不起作用。我不知道为什么。
  • 表现可能更好。作为用户,您可以看到笔记跳了下来。 (由于Firefox中的奇怪和不可预测的行为,我不得不将事件处理程序从document.ready移动到window.onload