Access to object property or method from appended element jquery - javascript

I would like to know if it's possible to access an object property from an appended element. For example:
function anyFct(){
this.div=$('<div ref="dv">').html('Hi').appendTo('body');
div.animal='dog';
div.yld=function(){
alert(div.animal);
};
$('input type="text" value="anyIn" onclick="yeldAnimal(this);"').appendTo(div);
}
function yeldAnimal(obj){
var actElement=$(obj).closest('div[ref=dv]');
actElement.yld(); // I want that this yields 'dog'
}
and my HTML:
<input type="button" value="test" onclick="anyFct();">
So this is the logic: I create a div element when the button is clicked on. This div element has a text that when clicked on calls an external function that calls a method on its parent element (the div).
For many contextual reasons this must be the logic. I've already found a solution that is saving the object div in a global array and then search in all values of the array for the object that triggered the method. However, I would like to know if there is a 'cleaner' or correct way to do this.

It's possible, and there are a couple of ways you could achieve it. The important thing you need to understand is the distinction between jQuery objects and actual DOM elements. When you use jQuery to create a <div> element, you create both; but what you end up with a reference to is the jQuery object - or, if you're chaining jQuery function calls, the result of the last function called. The DOM element, assuming you actually append it to the DOM, persists once that section of code has finished execution, but the jQuery object that's created will vanish when that variable goes out of scope.
When you execute some jQuery code later on to get a reference to your DOM element, it's referring to the same element on your page but it's a different jQuery object, so any custom properties you added to the original one won't be available. How do you get around that? Set the properties on the actual DOM element.
You can use the .get() method to access the underlying DOM element from a jQuery object, indexed from 0 (so .get(0) called on a jQuery object will return the first DOM element it references). With that you can then set your custom properties and later retrieve them, something like this:
function anyFct(){
this.div=$('<div ref="dv">').html('Hi').appendTo('body');
var elem = div.get(0); // the actual DOM element, the div
elem.animal='dog';
elem.yld=function(){
alert(elem.animal);
};
$('<input type="text" value="anyIn" onclick="yeldAnimal(this);"/>').appendTo(div);
}
function yeldAnimal(obj){
var actElement=$(obj).closest('div[ref=dv]').get(0); // also the div
actElement.yld(); // alerts 'dog'
}
jsFiddle demo
Note that I've made a few changes to your code in addition to adding in the usage of .get(), most notably correcting the syntax for creating the <input type="text"> element in the first function.

Okay, most of this is not syntactically correct javascript and seems to be overly complicated. I believe if I understand what you're trying to achieve you want the following:
function anyFct(){
var div=$('<div ref="dv">').html('Hi');
div.animal='dog';
div.yld=function(){
alert(this.animal);
};
var element = $('<input type="text" value="anyIn">');
$(element).click(function() {
div.yld();
});
$(div).append(element);
$('body').append(div);
}

Related

Using two getElementByIds

Is it possible to do something like:
this.getElementById('first-id').getElementById('second-id')?
When I do this, I get an error "getElementById(...).getElementById is not a function.
The reason I'm trying to do this is because I am able to console log the first-id element, but not the second (when I do this.getElementById('second-id')). I assume it is because my DOM is not completely loaded.
However, I thought since it loaded the first-id element, I'll be able to do another getElementById as the first part is loaded and the next element is nested/a child of the first-id element.
Can someone explain why this logic doesn't work?
An element with a given ID is supposed to be absolutely unique in a document. There should never be more than one element with the same ID. So, calling getElementById on an element to search among its children, if it were possible (which it isn't), wouldn't make a whole lot of sense - instead, search from the whole document. You should only need
document.getElementById('second-id')
Most answers here are correct and will help the OP implement the solution. This will try to answer Is it possible to do something like:, which is what OP asked originally:
this.getElementById('first-id').getElementById('second-id')
getElementById() is a Document interface method and is only available on the Document object. It is not available on the Element, and this.getElementById('first-id').getElementById will be equal to undefined. undefined is not a function and can't be invoked, hence the error.
It makes sense that getElementById() is only available in the Document interface as an ID is (ideally) supposed to be a unique in the DOM. So, providing the function inside other elements is not as useful.
querySelecetor() is both a document and element method and is available on both the objects.
That is why you can do something like :
document.querySelector('#first-id').querySelector('#second-id')
(Searches for #second-id child of #first-id)
You could possibly chain it using Document.querySelector(), but you should never need to use this as ids should be unique.
E.g, something like this:
var selected = document.querySelector('#first-id #second-id');
console.log(selected);
<div id="first-id">
<div id="second-id">1</div>
</div>
<div id="second-id">2</div>
<!-- Don't use duplicate ids - this is just for demonstration -->
You could do something weird like the following, but it's silly.
<script>
window.addEventListener("load", function() {
alert(document.getElementById("parent").ownerDocument.getElementById("child").textContent);
}, false);
</script>
<div id="parent">
<div id="child">Yo</div>
</div>

how to re-write string in jquery to avoid accessing the dom again, and use jquery selector

I am trying to re-write some jquery so that uses a selector I had previously created, so that it doesn't need to access the DOM again. The selector I had created already access the DOM once, and I want to use its contents in a string literal in a function.
My current code is the following:
$(this.$content[$(`.nav a[href$="${window.location.hash}"]`).parent().index()]).show();
which works just fine, but ".nav a" is accessing the DOM, which I do not want in this instance. I want to use this.$navigation, which I had created before and already has the information from the DOM. I tried writing it as
$(this.$content[$(`this.$navigation.find('a')[href$="${window.location.hash}"]`).parent().index()]).show();
where this.navigation = $("#main-nav"), the parent of the .nav elements, but it does not work in this way.
Any suggestions on how I might approach this?
The inner jQuery object should be moved outside of the string literal, and the attribute selector needs to be placed inside the find() call.
$(this.$content[$(this.$navigation).find(`a[href$="${window.location.hash}"]`).parent().index()]).show();
In addition, I would assume from the naming convention that $navigation already holds a jQuery object so does not need to be wrapped again. As such, this should work:
$(this.$content[this.$navigation.find(`a[href$="${window.location.hash}"]`).parent().index()]).show();

Javascript node empty even before appending it's contents to a different node

I'm creating a JQuery object(let's call it $dummyHTML) and setting some html content inside it. Then I go through each of it's child nodes including text ones, do some checks, and append them to a new different JQuery Object(let's call it $refinedHTML).
But the problem is that the contents of $dummyHTML seems to be empty even before I append them to $refinedHTML!
Now, I know that JQuery append function doesn't copy a node, it actually transfers the node to the other JQuery object. So I'm guessing the append function triggers before I mean it to?
Here is a minified example of the issue.
var $dummyHTML = $('<div/>');
$dummyHTML.html('Hello there, <span>myself!</span>');
var $refinedHTML = $('<div/>');
console.log($dummyHTML[0]);
$dummyHTML.contents().each(function() {
$refinedHTML.append($(this));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
But if I remove the .contents part the programs works as expected.
.contents() extracts the content of a DOM element .When you create an object on the fly,it is not yet a DOM element so .contents() will not work however you can manipulate the object data in other ways.
Reference here:
Given a jQuery object that represents a set of DOM elements, the .contents() method allows us to search through the immediate children of these elements in the DOM tree and construct a new jQuery object from the matching elements.

Click Listener from jQuery to Javascript

I work on a site that recently changed, I track certain clicks on the site through GTM and push it into the dataLayer for Google Analytics.
With the changes to the site I can't use jQuery any more so I'm having to change the following jQuery to Javascript, but I just can't get it to work. The script used to collect the h3 text within the div class 'grid_4' when the div was clicked on. The whole structure has changed now, but the old jQuery one looked like this;
<script>
var h3Tile = $("div[class*='grid_4'] a").find('h3').text();
$("div[class*='grid_4'] a").click(function() {
dataLayer.push({
'h3Value' : h3Tile,
'event' : 'tileClick'
});
});
</script>
The js I have so far is;
<script>
var outerElement = document.getElementsByClassName('ContentTeaser');
var childElems = outerElement.getElementsByTagName('h1').innerHTML;
var myFunction = function() {
dataLayer.push({
'h1Value' : childElems,
'event' : 'tileClick'
});
};
for(var i=0;i<childElems.length;i++)
childElems[i].addEventListener('click', myFunction(), false);
</script>
The only problem is that GTM refuses to accept this, saying;
'Uncaught TypeError: outerElement.getElementsByTagName is not a function'
Which I understand is related to the fact that I am creating an array rather than selecting an individual element, but I was hoping my for loop would handle this? or am I mistaken?
Thank you for any help anyone can offer.
Matt
getElementsByTagName is a method found on HTML Elements.
It and (more to the point) getElementsByClassName return an (array-like) HTML Collection, not a single HTML element.
You need to loop over outerElement and call getElementsByTagName on each element in turn instead of trying to call it on the collection itself.
Which I understand is related to the fact that I am creating an array rather than selecting an individual element, but I was hoping my for loop would handle this?
You have two collections. You are looping over the second one, but are trying to treat the first one as a single element.
It would probably be easier to simply use query selector instead:
var childElems = document.querySelectorAll(".ContentTeaser h1");
You then have a couple more problems:
for(var i=0;i<childElems.length;i++)
Since childElems is the value of innerHTML, it is undefined (if you'd called it on an element instead of an html collection then it would be a string instead) so that will throw an error.
Don't use innerHTML (which I already fixed in the query selector example).
childElems[i].addEventListener('click', myFunction(), false);
You are calling myFunction immediately and trying to assign its return value (undefined) as an event handler. Remove the ().

Functionality of Javascript functions and jquery selectors from within html

I have this javascript function:
function clearDiv(div_id) {
alert(div_id);
$(div_id).empty();
}
And the following html:
<input value="Clear" type="button" onClick="clearDiv(div_to_clear)" />
<div id="div_to_clear">
<!-- more html -->
</div>
Why is it that the above will actually find the javascript div object with id div_to_clear and use the $ selector to access it?
If I had done the input as :
<input value="Clear" type="button" onClick="clearDiv('div_to_clear')" />
I would need to change my function to
function clearDiv(div_id) {
alert(div_id);
$('#' + div_id).empty();
}
Is there any documentation about this? Are there advantages to using one method or the other?
IE (and Chrome in an effort for compatability) will create properties on the window with names of elements with ids, corresponding to those elements.
In the first example are are passing window.div_to_clear which points to your element directly. In the second, you are passing a string telling jQuery which element to select.
The first one is non-standard behavior so you should not rely on it.
The first one is actually a browser bug (they called it a feature) to access the DOM node by window.div_to_clear (the global variable) - and jQuery creates its wrapper around the element. For legacy reasons, it still exists in current browsers, but is deprecated. Your really should use the selector solution.
This is because there is a widely-implemented (although not best practice) method by browsers to automatically place html elements with id's into their correlating variable name. For example:
<div id="someId"></div>
will end up psuedo creating the variable someId which holds that html element.
These questions have similar information on the behavior:
Do DOM tree elements with ids become global variables?
Where does the variable holding an element with an id get stored?
Element accessible with ID
Here is a jsperformance test showing that accessing the id in that manner is slower than using document.getElementById: http://jsperf.com/global-id-vs-document-getelementbyid

Categories

Resources