jQuery: Detach without string selector - javascript

I'm trying to detach a DOM element to append it to another DOM element. But jQuery refuses to do anything, silently.
Thing is, I can't use a string selector, because I don't know how to select this element. I've stored it in a variable when I first appended some html code to the initial parent (through "appendTo".
this.element = $(my_html_string).appendTo(some_dom_parent);
And that works fine. The code that is not working as expected, is following:
this.transferTo = function(dom_parent)
{
$(this.element).detach()
$(this.element).appendTo(dom_parent);
}
What happens is:
The element is NOT removed from wherever it is.
The element IS appended to the new parent.
Previously bind click events are triggered on both elements.
That click event appends a popup dialog to the element. It's being appended to the element in the new parent, always, regardless which one I click.
I tried some hardcoded detach like:
$('#very_specific_id').detach()
... and it works. But thing is, I don't have IDs placed around, and sounds like a very bad way to do this.
So the problem seems to rely on the fact I'm saving a jQuery DOM Element and trying to use .detach from it, instead of using a $(".query") like everyone else.
Ideas? Workarounds? Thanks!

Try changing this:
this.transferTo = function(dom_parent)
{
$(this.element).detach()
$(this.element).appendTo(dom_parent);
}
to this:
this.transferTo = function(dom_parent)
{
var $thisElement = $(this.element);
$thisElement.detach()
$thisElement.appendTo(dom_parent);
}

Related

Is there anything wrong with adding an HTML element as a direct child of document.documentElement?

I'm writing a chrome extension, part of the functionality of which requires me to hide every html element at times with the exception of one div which I've created. (I hide everything and add the div to the current website in javascript) Because setting document.body.style.display = "none" will not allow any of the body's children to be seen, I need to add the child that I want to be seen somewhere else. (I also tried using style.visibility but for some reason that didn't hide certain HTML elements/backgrounds on certain pages.) My fix is to add the div to document.documentElement, outside of document.body. This fix actually works perfectly, but seems strange. Is there anything wrong with adding a new child to the elements? Am I doing something wrong?
EDIT: A few answers have used the children of document.body, so I thought I should note that my code has to run at document_start, and though I wait for document.body to load before executing, I can't wait for all of its children to load. Hence I can't use/store the children of document.body.
Also, I'm grateful for all the answers providing alternate solutions, they're quite useful. But out of curiosity, does anybody know if there's anything wrong with what I'm currently doing? Why is it working, if so?
The W3C specification of HTML document structure says that it consists of the <head> and <body> elements, and the <body> contains the content that's intended to be rendered. Nothing is stated about elements outside these two elements.
If it seems to work it's probably just an accident of implementation -- for instances, many implementations are forgiving of things like malformed HTML.
It's perfectly fine to append elements or text nodes directly to document.documentElement.
DOM is not HTML, it has its own specification, which - being an Object Model - is naturally quite permissive:
document.documentElement is an Element [spec]:
The document element of a document is the element whose parent is that document, if it exists, and null otherwise.
Elements are allowed to have these children [spec]:
Zero or more nodes each of which is Element, Text, ProcessingInstruction, or Comment.
Create a new DIV to hold the children of the body, and hide that.
var saveDiv = document.createElement("DIV");
saveDiv.id = "saveDiv";
saveDiv.style.display = "none";
Array.from(document.body.children).forEach(el => saveDiv.appendChild(el));
document.body.appendChild(saveDiv);
A potential solution:
const body = document.querySelector('body');
body.innerHTML = `<div class="my-div"></div><div class="content">${body.innerHTML}</div>`;
Now you have the body content all snug alongside your div, both of which you can hide/show. As pointed out below, I completely spaced that this will destroy your listeners. If you want to preserve listeners, try the following:
const body = document.querySelector('body');
const fragment = document.createDocumentFragment();
const hideBody = () => {
for (let el of [...body.children]) (fragment.append(el))
}
const showBody = () => {
for (let el of [...fragment.children]) (body.append(el))
}
Attach the appropriate one to whatever your event is. This will preserve all your listeners. If you need any functionality, DocumentFragment has the querySelector() and querySelectorAll() methods.
Another method is the modal method, where you just have a div that covers the whole page. Check out the Bootstrap modals, for example. If you initialize it with data-backdrop="static" and data-keyboard="false" then it won't disappear by clicking outside or hitting esc. The element can be selected with document.querySelector('.modal-backdrop'). Set the opacity to 1 and the background to white or whatever aesthetic you're going for.

Fire ajax call on onclick and div

I am attempting to fire off an AJAX call based on the onclick event for a google map integration. The info_window_content function seen here: http://jsfiddle.net/6xw2y/ is the call to create the divs that reside within the map itself.
The "v" variable does in fact contain a store_id. So in the opening line of that function, it has the following:
var info_window_string = "<div class='maps_popup' id="+v.id+">";
Now I have an onclick event that I have duplicated and modified. The first onclick event works just fine and refreshes the panel as it should. The second onclick event doesn't work and the code for that is below:
$("#div").click(function(){
var store_id = $(this).find("div").attr("id");
var pathname = "ajax=1&store_id="+store_id+"&action=get_nearby_stores&distance="+distance+"&lat="+lat+"&lng="+lng+"&products="+$('#edit-products').val();
$("#pocp_content").load("file1.php?" + pathname);
});
That doesn't seem to work. I've also tried changing the div tag to be like this:
$("div").click(function(){
Which still doesn't work. As an added side hint. At one point I was able to get it to refresh but it was passing map-container as the store_id, instead of the id itself.
What am I missing here?
I agree with Joke_Sense10,
but I think you're probably not binding the event to the right DOM element.
Try to open up the developer console in your browser (while being on the side you develop this code for), and enter $("#div") to see if the element it returns is the one you expect. You can also use console.log($("#div")) in the code for that.
answer in comments
For a larger number of elements, always use .on() method as the latter will bind an single event listener on one of the topmost nodes in the DOM tree.
$(document).on("click","#"+v.id, function(){

Jquery Class Selector Fails to Select new Class Instances Added with .append()

I'm semi-new to Javascript/jQuery so apologies in advanced if I'm missing something basic. I have a function that is triggered whenever a user types in an element with a specific class.
$('.relevantClass').keyup(function(){
//code...
});
Now this function may end up, depending on the situation, creating a good deal of new HTML including new instances of relevantClass through the .append() method.
var newHTML = <div class='relevantclass'>Content...</div>;
$('#wrapper').append(newHtml);
However, the jQuery selector does not seem to detect and execute the function when a user types in the newly created relevantClasses. I've checked the newly created Html and it has the correct class tags and old instances of the relevant class due work.
I'm guessing this has something to do with .append(); messing with the DOM and I need someway to "refresh" the selector and let it do its jQuery thing researching the DOM to find the new classes. Any thoughts on how to do this? Is there some jQuery method I can't find?
You have to use on() to attach events that work on dynamic content:
var $parent = $("selector"); //the element you're appending .relevantClass to
$parent.on("keyup",".relevantClass",function(){
//code...
});
Keep in mind that to work with dynamic content, you have to attach the event to relevantClass's closest parent that exists on page load.
Some people use body, but you should get used to using parent elements as close as you can get to the dynamic content. This is so that event delegation occurs on a smaller scale.
More info on on() here.
Also, I hope that newhtml variable is wrapped in quotes.
$('.relevantClass').on('keyup', function(){
//code...
});
Try something like
$('body').on('keyup', '.relevantClass', function() { ... }
The idea is that you use an existing root element and use your class selector as a filter. See the examples here.

Checking if node exists in Javascript

Here's the deal - I've got a load of elements on a page, and I'm using Javascript to remove some of them (this.parentNode.removeChild(this)) which is working great. However, if I have a variable referring to this node, then remove the node, the variable does NOT lose it's value! But if I then try and perform some other actions on this element, I get errors!
Example:
var element = document.getElementById('ooolookatmeimanelement');
element.parentNode.removeChild(element);
alert(element);
I still get "[Object HTMLblahblahblah]" in the alert, rather than null or undefined - anyone got any ideas how I can check to see if the node has been removed? It's probably something really simple that I'm oblivious to!
If you remove the node, remove the references too. E.g. in your code, assign null to element:
element = null;
Or if this is not possible, you can always check the value of parentNode. It should give null if the element is not part of the DOM:
if(element.parentNode === null) {
// element is not part of the DOM
}
But this does not make much sense to me (might depend on the context though): If you removed an element, then you know that you have removed it. Why would you do any further operations on it?
You can also use document.body.contains(element); or $.contains(body, element).
Checking for the parentNode is not reliable; if what is removed is an ancestor of the element, instead of the element itself, then parentNode will still return the parent, while the element is not in the DOM anymore.
var ancestor = document.querySelector('div');
var ghost = document.querySelector('br');
ancestor.parentNode.removeChild(ancestor);
console.log(ghost.id + ' son of ' + ghost.parentNode.id);
console.log(ghost.id + (document.body.contains(ghost) || ' dont') + ' exists in DOM');
<div id="ancestorToRemove">
<p id="a bitch">
<br id="you">
</p>
</div>
.removeChild() only removes the element from the DOM, and not from memory.
Hence it still exists until it's garbage collected. However you're holding a reference to it, so that won't happen.
Another point that may save time if you're needing to run JavaScript triggered by onClick which does something to itself, you can do this:
onClick="if(!document.getElementById(this.id))return false; YOUR CODE HERE"
For me, this came up when I had a DIV with several children, one of which was a "remove this div" button (actually a DIV made to look like a button). When clicked, this 'button' would remove the DIV from the DOM, as expected, but then would call the onClick event of the DIV itself (which no longer exists). This caused problems. By adding this to my DIV's onClick, I prevented running the code which refers to the DIV after its removal.

JQuery DOM creation/handling issue

So, say I have selected, in JQuery, a singular DOM div.
I then proceed to create a new DIV like so:
var DIV = $("<div>Hello, world</div>");
After that, I attempt to place that DIV inside the original one like so:
$(OriginalDiv).append(DIV);
Okay, that works.
Now I want to edit DIV further.
Calls to .click, .html, .addClass, (And likely more) do not work!
Okay, instead I do:
var OriginalDiv = $("someSelector");
var DIV = $("<div>Hello, world</div>");
DIV = $(OriginalDiv).append(DIV);
That appears to work at first; However, instead, it sets DIV to reference the same DOM object as OriginalDiv and NOT the newly appended DOM object. Naturally, this does not allow me to edit DIV.
So, then, I try two more methods:
var OriginalDiv = $("someSelector");
var DIV = $("<div>Hello, world</div>");
$(DIV).appendTo(OriginalDiv);
and
var OriginalDiv = $("someSelector");
var DIV = $("<div>Hello, world</div>");
DIV = $(DIV).appendTo(OriginalDiv);
Not even these work.
If I haven't done a very good job explaining, here is my exact dilemma
I am trying to create a DOM object in jquery, then append it to another DOM object using jquery. The problem is, once it gets appended, there seems to be no way for me to directly access it without using somethign like .children.
I'd like very much to be directly returned somewhere along in that process a reference to the DOM object which I am appending. As in the one that actually gets appended.
I'm not sure how to do this in JQuery. Anybody know a solution?
Thanks
--G
Yes, append won't work as it returns a reference to the element the new element was appended to. jQuery supports method chaining, so this should work easily:
$("<div>Hello, world</div>")
.click(function() {
// something
})
.appendTo('someSelector');
But even
var $ele = $("<div>Hello, world</div>").appendTo('someSelector');
will work. appendTo returns a reference to the element which was appended. If this does not work for you, you have your problem elsewhere.
Comments on your code: This is not your problem, however it is important for you to know what is going on here.
This part
var OriginalDiv = $("someSelector");
var DIV = $("<div>Hello, world</div>");
$(DIV).appendTo(OriginalDiv);
is the same as
$($("<div>Hello, world</div>")).appendTo($("someSelector"));
You see, you have a nested call to jQuery, because DIV is already a jQuery object. There is no need to pass it again to jQuery.
You can also pass a selector directly to appendTo.
you could try this;
var DIV = document.createElement('div');
then you can use;
$(div).html('Test!');
or what ever you want to use with.
You don't have to get anything back from the DOM. Once you create the element with jQuery, you already have a reference to the DOM element. Inserting it into the document does not do anything special.
// this will create the DOM element, and the jQuery
// object wrapping that newly created DOM object
// is assigned to DIV.
var DIV = $("<div>Hello, world</div>");
// Don't worry about getting a return value from this
// append() call. What we need is already available inside
// the variable DIV.
$(OriginalDiv).append(DIV);
// continue using DIV as you normally would. It is referring
// to the same DOM object that was just appended to the document.
DIV.addClass('green');

Categories

Resources