Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent overlapping while positioning element at height of another

Inside a long text document there are some "special words" to which I want to display notes/annotations on the left. Each note should be as close as possible to the level of the word it is refering to.

The HTML for this is organised in a table. Each paragraph is one table row, consisting on annotations in the left and main text in the right table column. the notes/annotations go to the left. However, unfortunately, there are also some other elements/text nodes in there.

<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>

It's easy to change the "note"-spans to absolute and positioned them on the level of their reference:

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

However, life is not so simple here. Since there could be a lot of reference words in one line (while on other there are none of them) I need a rather sophisticated way to distribute the notes so that they are as close as possible to their references without destroying anything in the layout (e.g. being placed outside of the table cell or overlapping with other elements).

Furthermore, the height of the table cells could not be changed. Elements which are not notes must not be moved. (Note elements are always in the order they appear in the main text. That's not the problem.) So, I need an algorithm like this:

  • Take all notes in a table cell.
  • Analyse blank space in that table cell: Which areas are blank, which are blocked?
  • Distribute the notes in the table cell so that each note is as close as possible to its reference word without any element colliding with any other item in the table cell.

Is there any fast and elegant way to do this without having to write hundreds of lines of code?

Here is a JSfiddle: https://jsfiddle.net/5vLsrLa7/7/

[Update on suggested solutions]

Simply setting the position of the side notes to relative or just moving notes down won't work, because in this case, the side notes will just go downwards relative to their desired position which results in side notes way to far from their reference words. After all, for a neat solution I need to side notes spread in both directions: up and down.

[Update] The expected result would be something like this: Expected result: note/reference placing

As you see, it's never possible to place all the notes at the height of their reference. However, the free space is used to position them as close as possible, moving them up and down.

like image 772
cis Avatar asked Nov 25 '15 12:11

cis


2 Answers

I changed move() function as follows:

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;
    });
}
like image 84
Miron Avatar answered Sep 29 '22 11:09

Miron


I'm assuming that your element's notes will be in the correct order always

I made some changes to your 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;    
}
like image 45
Erwin O. Avatar answered Sep 29 '22 10:09

Erwin O.