Подтвердить что ты не робот

Предотвращение перекрытия при позиционировании элемента на высоте другого

В длинном текстовом документе есть некоторые "специальные слова", на которые я хочу отображать примечания/аннотации слева. Каждая нота должна быть как можно ближе до уровня слова, на которое оно ссылается.

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 или просто перемещение заметок не будет работать, потому что в этом случае боковые ноты будут просто идти вниз относительно их желаемого положения, что приводит к тому, из их справочных слов. В конце концов, для аккуратного решения мне нужно, чтобы боковые заметки распространялись в обоих направлениях: вверх и вниз.

[Обновление] Ожидаемый результат будет примерно таким: Ожидаемый результат: примечание/размещение ссылок

Как вы видите, никогда невозможно разместить все заметки на высоте их ссылки. Тем не менее, свободное пространство используется, чтобы расположить их как можно ближе, перемещая их вверх и вниз.

4b9b3361

Ответ 1

Спасибо за все ответы и комментарии. Наконец, я смог выяснить, по крайней мере, частичное решение, которое работает для меня.

Прежде всего, я смог перестроить свой HTML, так что теперь элементы "non note" в левом td завернуты в один div, который теперь является самым первым элементом в td. Итак, теперь между нотами нет ничего, может быть, что-то перед ними.

Идея моего решения заключается не в том, чтобы дать заметкам новую позицию, а для каждого из них установить новый margin-top. Максимальное количество значений margin-top, добавляемых в ячейку таблицы, вычисляется до (называемое "пространство роуминга" ), являющееся пространством ниже последней ноты в ячейке таблицы. Таким образом, расположение таблицы не разрушается.

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();
});

Как вы заметили, одна из моих основных проблем все еще не решена: заметки только перемещаются вниз, никогда не поднимаются. Из-за различных проблем с моей реальной веб-страницей я еще не реализовал это. Тем не менее, алгоритм может быть чем-то вроде: если верхний верхний край больше высоты текущей ноты, а разница между выводом текущего якоря примечания и нижестоящим анкетом примечания меньше высоты текущей заметки, чем вычитаем высоту текущей ноты с нового поля.

Тем не менее остаются две проблемы:

  • Если окно максимизируется или быстро изменяется с довольно тонкой ширины на большую ширину, настройка позиций нот не будет работать. Я не знаю, почему.
  • Производительность может быть лучше. Как пользователь, вы можете видеть, как заметки спрыгивают. (Из-за странного и непредсказуемого поведения в Firefox мне пришлось переместить обработчик событий с document.ready на window.onload)

Ответ 2

Я изменил функцию 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;
    });
}

Ответ 3

Я предполагаю, что ваши заметки будут всегда в правильном порядке

Я внесла некоторые изменения в ваш 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;    
}