Unable to access elements in HTMLCollection from getElementsByClassName - javascript

I would like to get an element from an HTMLCollection. The return of document.getElementsByClassName is exactly what I was expecting but when I try to access any attributes of it, it looks like there is nothing there. Here is my code (this code is run in a .js file that I src into my index.html):
document.addEventListener('DOMContentLoaded', function () {
var code = document.getElementsByClassName('CodeMirror-code');
console.log(code);
console.log(code[0]); //undefined
console.log(code.length); //0
}
and here are the console logs :
HTMLCollection(1)
0: div.CodeMirror-code //this is the div I want to access
length: 1
__proto__: HTMLCollection
undefined
0
also, If I enter in the console:
var code = document.getElementsByClassName('CodeMirror-code');
code[0]
I get the return:
<div class="CodeMirror-code">...</div>
which is exactly what I am looking for, but that is not the result I get in the script.

CodeMirror adds it's various DOM elements to the DOM after the CodeMirror() constructor is called or after CodeMirror.fromTextArea() is called.
So you can't simply wait for the various DOM ready events in order to find the element you are looking for. You can either pass the constructor a function which you can then manually add the editor to the DOM and then do a search. Or setup a custom CodeMirror event listener.
CodeMirror initialization hook
CodeMirror.defineInitHook(function(cmInstance){
var codeDiv = document.querySelector('.CodeMirror-code');
console.log(codeDiv);
});
var myCodeMirror = CodeMirror(document.body);
CodeMirror manual adding
var myCodeMirror = CodeMirror(function(editor){
//add the editor to the DOM
document.body.appendChild(editor);
var codeDiv = document.querySelector('.CodeMirror-code');
//either of these will work
var codeDiv = editor.querySelector('.CodeMirror-code');
console.log(codeDiv);
});
Demo
CodeMirror.defineInitHook(function(cmInstance){
DoWork( document.querySelector('.CodeMirror-code') );
});
var myCodeMirror = CodeMirror(document.body);
function DoWork(codeDiv){
console.log(codeDiv);
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.38.0/codemirror.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.38.0/codemirror.min.js"></script>

You might be doing this code before the elements have been rendered on the screen. That's why when you do it in the console it works.
Here are two options:
Try changing the js code to happen onload of body. If you don't know what onload is check out this: https://www.w3schools.com/Jsref/event_onload.asp
Try changing the js code to happen when the DOMContentLoaded listener comes up. Learn about how that works here: https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded

Related

Best way to attach an arbitrary callback function to a dom element?

The following (vanillajs) code works fine
// library code:
let close_cb; // nasty global var...
...
let tree = document.createElement('ul');
tree.addEventListener('click', function(event) {
...
// let close_cb = tree.getAttribute('CLOSE_CB');
// let close_cb = tree.onchange;
close_cb(leaf.id, ', so there');
// user code:
function my_close_cb(id, msg) {
const footer = document.querySelector('footer');
footer.innerHTML = id + ' is closed' + msg;
}
// tree.setAttribute('CLOSE_CB',my_close_cb);
// tree.onchange = my_close_cb;
close_cb = my_close_cb;
However the commented-out s/getAttribute code fails, getAttribute puts full text of "function my_close_cb(..." in local close_cb.
The commented-out onchange hack actually works, but feels terribly dodgy to say the least, although it is certainly closer to what I'm after.
Note the "library code" is hand written and fully under my control, whereas "user code" is intended to be transpiled or otherwise machine-generated, so changing my_close_cb to accept a single event argument would be a complete non-starter.
What is the best way to attach an arbitrary callback function that accepts an arbitrary set of parameters to a dom element?
You can attach a plain json property to the DOM element.
document.body.callback = function cb(text) { console.log(text); };
document.body.callback("hello world");
Using tree.close_cb or any other property to store a function or anything else is totally fine, as the DOM is just a (persistent) tree of JavaScript objects. As such they behave like any other JS object, and properties can be added without any restrictions.

Assigning a jquery object to a variable doesn't work as expected

Can someone explain why one of these versions of code works and the other fails?
This doesn't work:
var classForSelectedElement = "hightlight";
var prevSelect = $(".form-element");
var $selectedElement = $("div").on("click", ".form-element",function(e){
prevSelect.removeClass(classForSelectedElement);
$(this).addClass(classForSelectedElement);
});
Whereas this works:
var classForSelectedElement = "hightlight";
var $selectedElement = $("div").on("click", ".form-element",function(e){
$(".form-element").removeClass(classForSelectedElement);
$(this).addClass(classForSelectedElement);
});
Why?
Presuming that the .form-element elements are appended to the DOM dynamically after load (due to your usage of a delegated event handler) then the first example doesn't work as you are attempting to retrieve the .form-element on load of the page before they exist.

Avoid wrong interpretation of source with document.createElement for dynamic sources

I have script that I would like visitors on my website to run when they load a web page. It looks like this:
window.onload = function(){
var pxl=document.createElement('img');
pxl.setAttribute('src', 'http://localhost:8080/getTrackingPixel')
document.body.appendChild(pxl);
}
Most of the times the source returns an image and it works fine. However, sometimes it returns this:
<html><body style="background-color:transparent"></body></html>
And I can't really change the fact that it might sometimes not return an image. How do I change the javascript so that it can handle the html response without any errors? It might be possible for me to predict when it happens though - but I haven't managed to find a good way to request the source and return the html either.
You can achieve it by using the javascript Image object which, unlike the createElement approach, allows you to fetch the src url before inserting the img in the DOM.
The onload event of the Image object won't fire if the loaded content isn't an img.
Here it is :
window.onload = function(){
var pxl = new Image();
pxl.onload = function(){
// is IMG
document.body.appendChild(pxl);
}
pxl.onerror = function(){
// is not IMG
// Meaning in your case : <html><body style="background-color:transparent"></body></html>
}
pxl.src = 'http://localhost:8080/getTrackingPixel';
}
(Note that your code also missed the semicolon ";" line 4)

event when adding element into document

Adding new html string into the page:
document.getElementById('container').innerHTML = '<div id="child"></div>';
Is there an event that let me know when child element is in the document?
I have a function, which return some html codeas a string. And when this html will be added in the document, I need to execute javascript function. I've tried to use inline onload event
document.getElementById('container').innerHTML = '<div id="child" onload="console.log(\'ready!\');"></div>';
but it does not seem to work.
UPDATE:
Probably, I should provide more details about the situation. I have a library function
myLibrary.getHtml()
In old version, users just call this function and append the result into the document:
document.getElementById('container').innerHTML = myLibrary.getHtml();
In new version, the result is not a plain html. Now users can interact with it after they append it in the document. So after they append html into the document, I need to go through the result DOM and attach event handlers, hide some elements and other things. That is why, I need to know when they add it in the document and execute a javascript function, which turn plain html into fancy interactive widget.
You could try using DOM Mutation Events, but they are still inconsistently implemented across browsers.
If i have not misunderstood the question, you can probably get this to work
document.getElementById('id').childNodes;
If you can use jQuery, you could write your own custom event which would call the function you need to call whenever the new html has been added:
$('#container').bind('changeHtml', function(e) {
// Execute the function you need
});
And of course instead of just adding the html to the element, you would need to wrap it up in a function which triggers your custom event. Then all you'd need to do is call this function instead of setting the innerHtml yourself.
function addHtml(html) {
$('#container').innerHTML = html;
$('#container').trigger('changeHtml');
}
Solved by myself this way:
myLibrary.getHtml = function() {
var html;
...
var funcName = 'initWidget' + ((Math.random() * 100000) | 0);
var self = this;
window[funcName] = function() { self._initWidget(); };
return html + '<img src="fake-proto://img" onerror="' + funcName + '()"';
}
The idea behind the code is that I specify incorrect url for the src attribute of the img tag and the image triggers error event and call my function. Quite simple :)

Identifying the anchor tag when href is called

How can you identify in a function whether it has been invoked by an anchor tag href?
The event is null in this case, so event.target and event.srcElement won't work.
Code
HTML
Href works here
JavaScript
function SomeFunction ()
{
// I need to get the anchor element that invoked this function
}
What about
Href works here
function SomeFunction(context) {
var callingElement = context;
}
Following what #alex suggested, can you add a script to run in the page load to change the hrefs to be what you want (adding the 'this' reference)?
Take the following script for example, this will change the href value for anchor tags with id set to SomeID or class set to SomeClass:
function changeLinks() {
var all_links = document.getElementsByTagName("a");
for (var i=0; i<all_links.length; i++){
if (all_links[i].id == 'SomeID' || all_links[i].className == 'SomeClass') {
all_links[i].href = 'SomeFunction(this);';
}
}
}
Hope this helps...
Edit:
Following your comment, you can try this:
var clickedAnchor = null;
function setClickedAnchor(obj) {
clickedAnchor = obj;
}
function changeLinks() {
var all_links = document.getElementsByTagName("a");
for (var i=0; i<all_links.length; i++){
if (all_links[i].id == 'SomeID' || all_links[i].className == 'SomeClass') {
all_links[i].href = 'setClickedAnchor(this);' + all_links[i].href;
}
}
}
As far as I know there are only two ways to accomplish this using standard Javascript. The first way has already been outlined -- pass the node as a parameter to the function. The other way would be to handle the onclick event of the anchor. This is very similar to the previous approach in that is requires that you modify the markup to pass a parameter to a function. To do this you'll need to change the markup to the following:
Href works here
function SomeFunction(event) {
var node = event.srcElement;
}
The above code would pass the event object along to the function which would give you all sorts of interesting information about the event.
If you're unable to change the markup that is sent to the browser, you might want to consider using something like JQuery or another AJAX library to search for and modify the event handlers of the nodes at runtime. Modifying the markup before it gets to the browser is obviously preferred, but sometimes you don't have a choice.
Lastly, if you cannot change the markup and don't want to modify the DOM at runtime, you can always see what Javascript engine specific features are available. For example, you might be able to make use of arguments.caller in those engines that support it. I'm not saying that it will work, but you might want to see what's available.

Categories