Can't find element in DOM after loading it with ajax (want to bind jquery plug in to it)

24,059

Solution 1

The issue with plugins.

You're mixing lots of external libraries and code. This results in possible mis-matches between versions, and a lot of black boxes in your code.

As a developer, this should make you feel very uneasy. Having code you do not fully understand in your code base can get really frustrating really fast.

The alternative.

Often, these sort of plugins provide functionality we, as JavaScript developers can accomplish just as easily without them. This development process, in simple enough scenarios, lets us create code we understand and have an easier time maintaining. Not only do we learn from this process, but we also create smaller bits of specific code. Community driven solutions are very good in general, but it's important to remember they're not a silver bullet. Often you're stuck using a not-so-active project which has a bug for your specific case and you have to dig through a large, unfamiliar code base.

Your code

So what does this drag and drop plugin do?

Well, I'd break it down as the following:

  • Listens to the mousedown event on table rows
  • When such an event fires, start moving the table row to match the mouse position
  • When mouseup occurs, detect that, and finalize the position.

Let us see how we can do something similar. Let's assume the table's HTML is something like:

<table>
    <tbody>
    <tr>
        <td> Hello 1</td>
    </tr><tr>
        <td> Hello 2</td>
    </tr>
    </tbody>
</table>

Here is a fiddle with the table with some basic styling applied

Next, we'll listen to the selection events. We'll add an event to the table rows for selection and to the document to when the mouse is up. jQuery has event listeners for such events. Since we want these events to stick even after AJAX, we'll use .on which lets us use delegated events. .on means that even if we add content to the table later, it won't matter.

var selected; // currently selected row
$(document).on("mousedown","#MySpecialTable tr",function(){
   $("#textDiv").text(this.textContent); 
    selected = this;
});
$(document).on("mouseup",function(){
    $("#textDiv").text("Left "+selected.textContent); 
    selected = null;
});

Here is a working fiddle of such code.

Now, we'll want to actually change the drag&drop to work when, that is, update the current position to the one reflecting the mouse position. We can listen to mousemove events, and detect the element we're currently on. Something like

$(document).on("mousemove",function(e){
    $("#textDiv").text($(e.target).html());
});

You can see a working fiddle here

That's nice, but we want to actually change the element position. So we'll need to change the table structure to allow that. We can remove the element, and append it at the correct position. We'll check if we have a selected element, and if we do, we can track it compared to the current element in the mousemove event. We can for starters detect if we should drag with something like:

$(document).on("mousemove",function(e){
    if(selected !=null){// got an element selected
        if($("#MySpecialTable").has(e.target).length > 0){ //in the table
            $("#mousePos").text("DRAGGING");    
        }
    }else{
        $("#mousePos").text("NOT SELECTED");
    }
});

(Fiddle)

Now, we'll add actual selection, we'll replace the elements when the target is not our element and we're in the table. Our full code should be something like:

var selected;
$(document).on("mousedown","#MySpecialTable tr",function(e){
    e.preventDefault();//stop the text selection;
   $("#textDiv").text(this.textContent); 
    selected = $(this);
    selected.find("td").css("background-color","#999");
});
$(document).on("mouseup",function(){
    $("#textDiv").text("Left "+selected.text()); 
    selected.find("td").css("background-color","");
    selected = null;
});
$(document).on("mousemove",function(e){
    if(selected !=null){// got an element selected
        if($("#MySpecialTable").has(e.target).length > 0){ //in the table
            var el = $(e.target).closest("tr");//the tr element we're on
            el.before(selected);// replace the elements
        }
    }else{
        $("#mousePos").text("NOT SELECTED");
    }
});

$("#MySpecialTable").on('selectstart', false);//Don't let the user select the table

(Fiddle)

Now, so far we only have a few lines of code, which is nice since we know exactly what's going on and didn't need to use lots of lines of external code we don't fully understand.

But will it AJAX?

Let's load the data into the table with AJAX and see! We'll simulate an AJAX response using a setTimeout which would allow us to simulate an asynchronous request. We'll use

setTimeout(function(){
    $("#MySpecialTable").html("<tr><td> Hello 1</td></tr><tr><td> Hello 2</td></tr><tr><td> Hello 3</td></tr><tr><td> Hello 4</td></tr><tr><td> Hello 5</td></tr><tr><td> Hello 6</td></tr>");
},1000);

This means, update the HTML of #MySpecialTable after one second. Let's see if it works shall we?

So why does it work? well, we used delegated events which means we don't care if the elements we're loading are in the screen right now or not. We had the insight to do this since we built our code ourselves and knew what our final goal was. The only thing left to do is clean the code a little.

We'll wrap our code in the following, to prevent $ from being an issue in non-conflict mode (that is, $ is already taken in the page:

(function($){

})(jQuery);

Next we'll add a binding for our table event:

$.GertVDragTable = function(elementSelector){ // rest of code.

Eventually, our code might look something like this.

Using it, would be a simple $.GertVDragTable("#MySpecialTable"); alternatively, we can put it on $.fn and allow every function to call it. Which is a matter of taste.

No copy-pasta please :) I'd appreciate it if you stop on every stage and think why the next step was taken.

Solution 2

You don't need to use the ID as a selector, you can use any expression that can find your table.

If there's only one table on the resulting $.ajax call, you can search for "a table inside the container", using the container ID, which won't change:

$('#diagnoses_zelf table').tableDnD( ... );

If there's more than one table, use a different kind of selector, instead of the ID. A CSS class works fine:

$('table.table-diagnose').tableDnD( ... );

So does a data- attribute:

$("table[data-diagnose]").tableDnD( ... );

Solution 3

If your id is changing you should not use an ID then:

<table class="tableDiagnose table table-hover">

Plugin

function initialize_table() 
{
    $('.tableDiagnose.table').tableDnD({    
        onDrop: function(table, row) {
        alert(table + "     "  +  row);
        },
        dragHandle: ".dragHandle"
    });   
    DP('nee');  
}

EDIT: ajax is asynchronous :

function load_table()
{    
    DP('load_table');
    $.ajax({
            //async: false,
            type: "POST",
            url: "/diagnose_hoofdpagina/table_diagnose/" + DosierID, // <== loads requested page
            success: function (data) {
                    $("#diagnoses_zelf").html(''); //<== clears current content of div
                    $("#diagnoses_zelf").append(data).trigger('create'); // <== appends requested page
                    update_table(); 
                    initialize_table();    // <== calls jquery plug in
                },
            error: function(){
                alert('error');
              } 
        });
        //removed .done as you already have a success option in ajax


    return false;   
}

EDIT: found your bug........ you retrieve the table id then select it in $(tableid) but you missed the #

function initialize_table() 
{
    /*
    var tableid = $('#diagnoses_zelf table').attr('id'); //< this finds the correct table thanks to Fábio Batista => this option worked, rest didn't
    alert(tableid);
    // but you really should limit the use of variables when you don't need them*/
    //$('#'+tableid).tableDnD({            
    //like this directly
    $('#diagnoses_zelf table').tableDnD({    
        onDrop: function(table, row) {
        alert(table + "     "  +  row);

        },
        onDragStart: function(table,row){
        var tette = $(row).index;
        //alert(tette);
        },
        dragHandle: ".dragHandle"
    });     
}

See the demo here

EDIT

Solution 4

Try adding a title to your table, like so:

<table id = "tableDiagnose" class = "table table-hover" title = "table-content">

Then use the jQuery attribute selector to find this table instead of finding it by id.

$('table[title="table-content"]').tableDnD({ 
// the rest of your code
Share:
24,059
GertV
Author by

GertV

If you are retarded enough, you can downvote everything you see on stackoverflow. albert einstein

Updated on December 17, 2020

Comments

  • GertV
    GertV over 3 years

    So I have 2 html pages. 1 that functions as container and 1 that functions as content. When I load the content page with a table I'm able to use drag and drop.

    But when I go to my container page and load the content page into a div with ajax, the drag and drop stops working. All other javascript functionalities inside the content page still work. How can I bind the jquery dnd plugin to the table loaded with ajax?

    I'm using drag & drop with this as tutorial http://isocra.com/2008/02/table-drag-and-drop-jquery-plugin/

    my code looks like this:

    $(window).load(function()
    {   if(temp == 0)
        { 
            DP("eerste keer")
            load_table(); 
            temp = 1;
        }
    } );
    
    function load_table()
    {    
        DP('load_table');
        $.ajax({
                //async: false,
                type: "POST",
                url: "/diagnose_hoofdpagina/table_diagnose/" + DosierID, // <== loads requested page
                success: function (data) {
                        $("#diagnoses_zelf").html(''); //<== clears current content of div
                        $("#diagnoses_zelf").append(data).trigger('create'); // <== appends requested page
                    },
                error: function(){
                    alert('error');
                  } 
            }).done(function() {
            update_table(); 
            initialize_table();    // <== calls jquery plug in
            });
    
        return false;   
    }
    
    
    function initialize_table() 
    {
        var tableid = $('#diagnoses_zelf table').attr('id'); //< this finds the correct table thanks to Fábio Batista => this option worked, rest didn't
        alert(tableid);
        $(tableid).tableDnD({    
            onDrop: function(table, row) {
            alert(table + "     "  +  row);
    
            },
            onDragStart: function(table,row){
            var tette = $(row).index;
            alert(tette);
            },
            dragHandle: ".dragHandle"
        });     
    }
    

    enter image description here

    How is this possible and what can I do about it? Can anyone help me with this please.

    Very short: I want access to the ID of the table I load into my container page with ajax and use the jquery drag and drop plug in on it.

    EDIT

    Findings:

    Somehow my table in the container page got renamed to pSqlaTable instead of the id I gave to it in the controller page which is.

    <table id="tableDiagnose" class="table table-hover">
    

    Thats why the code couldn't find the table annymore Got fixed by this code thanks to Fábio Batista:

    $('#diagnoses_zelf table').tableDnD( ... );
    

    , but how can I use the dnd plugin now ?

    It finds the table now, but I'm still not able to bind the dnd plugin to it, Am I able to bind a jquery plug in to ajax loaded tables ?

    EDIT

    //drag & drop http://isocra.com/2008/02/table-drag-and-drop-jquery-plugin/
    function initialize_table() 
    {
        var tableid = $('#diagnoses_zelf table').attr('id');
        alert(tableid);
        $('#' + tableid).tableDnD({    
            onDrop: function(table, row) {
            alert(table + "     "  +  row);
    
            },
            onDragStart: function(table,row){
            alert('issemer?');
            },
            dragHandle: ".dragHandle"
        });    
    } 
    

    This is the code i'm still stuck with. tableid is correct but the initialisation of the jquery isn't. I can't drag the drows in the table. Is my syntax wrong ?

    EDIT

    Could it be that I can't bind the jquery to the table because I dynamicaly generate the table on the other page with ZPT (or javascript) ?

  • Ghazanfar Mir
    Ghazanfar Mir almost 11 years
    Keep in mind data attribute is HTML5 attribute and isn't supported by all browsers.
  • GertV
    GertV almost 11 years
    Firstly I added it into my content page. Everything worked there untill I loaded it into my container page. I transfered the code to my container page now. The script itself is callable, thats why I don't understand why I can't bind it to the table. Am I able to bind a jquery plug in to loaded content ?
  • GertV
    GertV almost 11 years
    It's a nice piece of code, but the suggestion of Fábio Batista workerd fine for me. $('#diagnoses_zelf table').tableDnD( ... );
  • GertV
    GertV almost 11 years
    thanks for your input, I used $('#diagnoses_zelf table').tableDnD( ... ); to fix the (can't find my table id problem)
  • TecHunter
    TecHunter almost 11 years
    I've looked a bit into the code, try $.tableDnD.makeDraggable($('#diagnoses_zelf table'))
  • GertV
    GertV almost 11 years
    where should I put this because I can't see how this would help me bind the plugin to the table.
  • TecHunter
    TecHunter almost 11 years
    @GertV ah sry I got it wrong. It's because you init the table before the ajax has done its work. make the ajax synchronous with option async:false or move the init code inside the success (prefered solution`. see my edited answer
  • Igarioshka
    Igarioshka almost 11 years
    @GertV sure you can. if the id is correct, the content is loaded fine, and the plugin itself is loaded and...'plugable'. you may want to try to load the content and try to bind the plugin from developers console. to check if the plugin is loaded, just try to call $().tableDnD, it should input the function string, if it does, try to call the initialize_table(), if the plugin (after the call) is bounded, then it may be a problem of synchronization.
  • GertV
    GertV almost 11 years
    I know what you are trying to do, but It doesn't work. I've already tried that. I added the .done because it wouldn't initialize the table when I put initialize_table() in the succes.Both when the ajax request was assynchronous and not.
  • TecHunter
    TecHunter almost 11 years
    then it's weird... maybe you have an error inside the success handler. I think removing it from success would hide the true error. Or move everything inside .done
  • GertV
    GertV almost 11 years
    The only thing that's in my succes is adding the table to my page. I thought that if I waited for the ajax call to be done to initialize the table it wouldn't give me this problem.
  • GertV
    GertV almost 11 years
  • GertV
    GertV almost 11 years
    The only thing that's in my succes is adding the table to my page. I thought that if I waited for the ajax call to be done to initialize the table it wouldn't give me this problem. From what I have now I can tell the plugin can find my table because it updates my mouse pointer to a "dragable icon" + the table is initialized in the DOM because I can find the table ID. I guess I just can't get the rows draggable at this moment.
  • TecHunter
    TecHunter almost 11 years
    @GertV got it working. but again please use success:function(){} OR .done(function(){}) but not both or it gets messy
  • GertV
    GertV almost 11 years
    What do you mean u got it working ? I placed all of it in succes now.
  • TecHunter
    TecHunter almost 11 years
    @GertV see my edit section. you forgot the # in the table id selector
  • GertV
    GertV almost 11 years
    I don't know how that works in your fiddle, I'm using exact the same code. I was first using $('#tableDiagnose').tableDnD so that alone can't be it :/
  • GertV
    GertV almost 11 years
    I'll let you know my findings, I'm gonnafully review my code again now since it does work in your example.
  • TecHunter
    TecHunter almost 11 years
    @GertV I've deleted the update_table() because you didn't provide it. Any console error? (F12 to get the console)
  • GertV
    GertV almost 11 years
    Doesn't give anny error, I realy don't know why it isn't working like it should.
  • GertV
    GertV almost 11 years
    it helped me a little, It didn't fix the problem so i'm not going to accept it as an answer. sry.
  • GertV
    GertV almost 11 years
    Not the answer to how to implement the jquery. But helped me do the trick of starting my own dnd. Thanks for the help and the good post.