Using document.addEventListener breaks document.getElementById - javascript

really hope someone can help me out here. Cutting a very long story short, on a few replies on this site I've seen people write that we should move away from:
<body onLoad="init();">
In the HTML to:
document.addEventListener("DOMCONTENTLOADED", init, false);
In the JavaScript file so we aren't mixing interactive code with content code etc.
But by switching to this method my code breaks, I can no longer access the DOM tree, here is an example:
function Tester(){
this.value1 = 10;
this.container = document.getElementById("fred");
this.list = this.container.childElementCount;
this.in_func = function(){
alert(this.value1+" "+ this.list);
};//end of this.in_func
}//end of function Tester
function init(){
var alpha = new Tester();
alpha.in_func();
}//end of function init
document.addEventListener("DOMCONTENTLOADED", init(), false);
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Untitled Document</title>
</head>
<body><!--onLoad="init();"-->
<section id="fred">
<section id="elem1"></section>
<section id="elem2"></section>
<section id="elem3"></section>
<section id="elem4"></section>
<section id="elem5"></section>
</section>
</body>
</html>
The this.container is always null so the childElementCount generates an error of:
"Uncaught TypeError: Cannot read property 'childElementCount' of null"
Yet when I comment out the event listener and use the onLoad technique it works, am I just doing something stupid? I've tried using a variable instead of using this.list, tried using querySelector instead of getElementById, I've tried "load" instead of "DOMCONTENTLOADED" but nothing seems to work.
I know it will be something really simple but I cannot find the solution anywhere online, maybe I am just searching for the wrong thing.
Please put me out of my misery.
thanks
Zen

This is the correct way of doing it:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Untitled Document</title>
<script>
function Tester(){
this.value1 = 10;
this.container = document.getElementById("fred");
this.list = this.container.childElementCount;
this.in_func = function(){
alert(this.value1+" "+ this.list);
};//end of this.in_func
}//end of function Tester
function init(){
var alpha = new Tester();
alpha.in_func();
}//end of function init
document.addEventListener("DOMContentLoaded", function(event) {
init();
console.log("DOM fully loaded and parsed");
});
// document.addEventListener("DOMContentLoaded", init(), false);
</script>
</head>
<body>
<section id="fred">
<section id="elem1"></section>
<section id="elem2"></section>
<section id="elem3"></section>
<section id="elem4"></section>
<section id="elem5"></section>
</section>
</body>
</html>
In you code you called init() and then passed it, but you should passed it as a function! document.addEventListener("DOMContentLoaded", init, false);
That's why

document.addEventListener("DOMCONTENTLOADED", init(), false);
Problem 1
You are calling init immediately and trying to use its return value as the event handler.
Remove the ().
Problem 2
Event names are case sensitive. It is DOMContentLoaded not DOMCONTENTLOADED

Related

Drawing .svg with javascript module "RDKIT-JS"

first of all I'm relatively new to javascript. So I'm sorry if my question is dumb. I would like to draw a molecule on my webiste by using this tool https://github.com/rdkit/rdkit-js. I also found an example here https://iwatobipen.wordpress.com/2021/12/29/create-desktop-chemoinformatics-application-with-js-chemoinformatics-rdkit-js/comment-page-1/. This example works in my case but when i try to invoke a function to draw a molecule as a .svg without using the example code, I fail. I get this error-message in my browser:
Uncaught ReferenceError: RDKit is not defined
at drawmol (results:21:15)
at results:33:5
In the following code example you can see the first case where it works and the second case were it doesn't. In both cases i use the same function.:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://unpkg.com/#rdkit/rdkit/dist/RDKit_minimal.js"></script>
<title>Document</title>
<script>
window
.initRDKitModule()
.then(function (RDKit) {
console.log("RDKit version: " + RDKit.version());
window.RDKit = RDKit;
})
.catch(() => {
});
</script>
<script> var drawmol = function() {
var mol = RDKit.get_mol("C1=CC=C(C=C1)O"); // the string here is a string representation of chemical molecules, it could also be something like "CO" or "CCCCC", shouldnt be important
var svg = mol.get_svg();
document.getElementById("drawer").innerHTML = svg;
};
</script>
</head>
<body>
<button type='button' onclick='drawmol()'> <!-- this works -->
draw
</button><br>
<script>
// drawmol() //this doesnt work
</script>
<div id='drawer'></div>
</body>
</body>
</html>
Later i would like to use the module to dynamically make those images. I use django as the framework. In this case i tried to present a minimal example without the django stuff.
Thanks in advance for your effort!
You are calling drawmol() before RDKit is ready.
To fix this, place it after RDKit is loaded:
window
.initRDKitModule()
.then(function (RDKit) {
console.log("RDKit version: " + RDKit.version());
window.RDKit = RDKit;
//Now RDKit is loaded you can safely call drawmol()
drawmol();
})
.catch(() => {
});

Remove self element onclick

I have an element that is added using javascript to my HTML document. I am trying to remove it when I click on that same element. I do not know the id of the element yet, I have just included a sample id for now.
I have already tried looking at this answer here (Creating an element that can remove it self?) and tried using the method provided here (Javascript: How to make a Control send itself in a method) as well to reference the element but have had no luck.
Here is what I have so far.
function remove() {
var element = this.id;
//I have also tried using document.getElementByID(this.id)
element.remove();
//I have also tried using element.parentNode.removeChild(element); to remove the element.
}
<div id="i" onclick="remove(this)">Sample text</div>
I am not sure what I am doing wrong but this is the error I keep getting.
"Uncaught TypeError: Cannot read property 'remove' of undefined"
You have to pass this through the function call in html onclick so it could refer to the element then you have to use it as parameter in the function definition, otherwise, if you use this in the function definition, it will just refer to the window object.
This will work fine
function remove(el) {
var element = el;
element.remove();
}
<div id="i" onclick="remove(this)">Sample text</div>
Minimalist solution:
<div onclick="this.remove()">Sample text</div>
your remove function should be like this
function remove(elem){
elem.parentNode.removeChild(elem);
}
your are passing a this in your html, which is the html tag itself, however, when you using this in your js function, that this is the function itself, so you will get error by trying to use the js function's id as element id
"Uncaught TypeError: Cannot read property 'remove' of undefined"
This means the object you're trying to remove doesn't exist what makes complete sense because this.id isn't defined anywhere.
To correctly reference a html element using javascript you need to use the document.getElementById() function. The id of the html element you're trying to remove is i - so try document.getElementById("i");
function remove() {
var element = document.getElementById("i");
element.remove();
}
<div id="i" onclick="remove(this)">Sample text</div>
Another - more elegant way - is to pass a reference to the object you clicked on with the callback function. This is simply done by adding event as a parameter. Inside the callback you can reference the element using e.target.
function remove(e) {
var element = e.target;
element.remove();
}
<div id="i" onclick="remove(event)">Sample text</div>
To match your query, using outerHTML will remove the elements and his components from the DOM.
This require to use document.getElementById().
function remove(me) {
document.getElementById(me).outerHTML = ""
}
<div id="i" onclick="remove(this.id)">Sample text</div>
A better coding practice, as the post is upvoted:
Using 3 chars for Id of elements is better
We can filter elements by type with target.nodeName, but the type need to be written in uppercase.
document.body.addEventListener("mousedown", function(e) {
console.log(e.target.nodeName, e.target.id)
if (e.target.nodeName === "DIV"){
document.getElementById(e.target.id).outerHTML = ""
}
}, false)
<body>
<div id="el1">Sample text</div>
<div id="el2">Sample text</div>
<div id="el3">Sample text</div>
<div id="el4">Sample text</div>
<div id="el5">Sample text</div>
<div id="el6">Sample text</div>
</body>
If you want to use this in normal JS function you'll need to bind this to function. Else it'll default to window object, this in normal js points to window object. If you want to use this in to point to an object invoking the function then use ()=>{} arrow functions
function remove(element) {
console.log(this) //will log Window Object
//I have also tried using document.getElementByID(this.id)
element.remove();
//I have also tried using element.parentNode.removeChild(element); to remove the element.
}
<div id="i" onclick="remove(this)">Sample text</div>
first grab your element:
const myDiv = document.querySelector('div');
then add click event listener to it, so when it gets clicked on, the callback method gets invoked, where 'e.target' acts as 'this', and finally remove it using remove():
myDiv.addEventListener('click', function(e){
e.target.remove();
});
Don't use inline event listeners (the one on the button in this example is only for simplification).
function createDiv() {
let div = document.createElement('div');
div.textContent = `Sample text, created on ${new Date()}`;
div.addEventListener('click', remove);
document.body.appendChild(div);
}
function remove(e) {
e.target.remove();
}
<button type="button" onclick="createDiv()">Add a div</button>
If you cannot access the element for adding a listener, you can also use a delegate listener:
document.addEventListener('click', remove);
function remove(e) {
// you need some check here if you don't want any element to be removed on clicking it
e.target.remove();
}
<div>foo</div>
<div>Sample text</div>
I would do something like this, so your JavaScript and CSS are cached. Just make sure you change your filenames when updating your files upon deployment (when people know your site exists).
//<![CDATA[
/* external.js */
var doc, bod, M, I, S, Q;// for use on other loads
addEventListener('load', function(){
doc = document; bod = doc.body;
M = function(tag){
return doc.createElement(tag);
}
I = function(id){
return doc.getElementById(id);
}
S = function(selector, within){
var w = within || doc;
return w.querySelector(selector);
}
Q = function(selector, within){
var w = within || doc;
return w.querySelectorAll(selector);
}
function remove(e){
e.parentNode.removeChild(e);
}
var sample = I('sample');
sample.onclick = function(){
remove(this);
}
}); // end load
//]]>
/* external.css */
*{
box-sizing:border-box; padding:0; margin:0;
}
html,body{
width:100%; height:100%;
}
body{
background:#ccc;
}
#content{
padding:7px;
}
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>
<head>
<meta charset='UTF-8' /><meta name='viewport' content='width=device-width, height=device-height, initial-scale:1' />
<title>Test Template</title>
<link type='text/css' rel='stylesheet' href='external.css' />
<script type='text/javascript' src='external.js'></script>
</head>
<body>
<div id='content'>
<div id='sample'>Sample Text</div>
</div>
</body>
</html>

How do I use JQuery inside an object constructor in my own javascript namespace?

I can't even find a hint of an answer to this question anywhere, so maybe I'm totally barking up the wrong tree...
I'm making a javascript namespace, inside of which I am putting a constructor function:
var SEP=SEP||{};
SEP.person=function(name)
{
this.name=name
this.sayName=sayName
function sayName()
{
return this.name;
$(document).ready
(
function()
{
$('body').css('background', 'red');
}
);
}
}
Then I am calling the function from the HTML...
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>test</title>
<meta name="generator" content="BBEdit 10.5" />
</head>
<body>
<script src="libraries/jquery/javascript/jquery-1.11.0-min.js"></script>
<script src="libraries/sep/javascript/sep.js"></script>
<p>Hello</p>
<script>
var bob=new SEP.person("bob");
word=bob.sayName();
document.write(word);
</script>
</body>
</html>
Question one is, why doesn't this work?
Question two is how can I make it work?
Question three is, if I want to make a more involved constructor, with JQuery peppered thoughout, do I need to do the document ready thing over and over, or is there a better way?
Many thanks in advance.
Actually your constructor is working fine. But you should not use document.write in your code. Instead you can append the value to a html tag, using either .append() .html() or .text() methods. Also there should be a small change in your sayName() method, Css should apply before return. Otherwise it will not hit on those lines of code.
var SEP = SEP || {};
SEP.person = function (name) {
this.name = name
this.sayName = sayName
function sayName() {
$('body').css('background', 'red');
return this.name;
}
}
var bob = new SEP.person("bob");
word = bob.sayName();
$("span").text(word);
Demo

call a javascript function inside a div

I would like to create a webpage which contains several divs each containing the same draw function with different implementation (like a generic interface). After loading the page I want to iterate through all the divs and call each draw function one after the other.
My page so far looks like this:
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
<script language="javascript" type="text/javascript" src="jquery-1.8.2.js"></script>
</head>
<body>
<script type='text/javascript'>
$( document ).ready( function() {
// Draw all slots
$('div.slot').each(function(i, d) {
console.log('slot found: ' + d.id);
// d.draw() does not work
draw();
});
});
</script>
<div class="slot" id="slot1">
<script type='text/javascript'>
function draw() {
console.log('Here we draw a circle');
};
</script>
</div>
<div class="slot" id="slot2">
<script type='text/javascript'>
function draw() {
console.log('Here we do something totally different and draw a rectangle');
};
</script>
</div>
</body>
</html>
Unfortunately I don't know how to call the draw function of the selected div "d".
Right now it only calls the last defined draw function.
Update:
Mind you that I can not combine the different draw methods into one which would get a parameter like shape handed in. The draw methods will be totally independent from each other.
Why are you defining scripts in the divs?
Do your logic all in one script block:
<head>
<script language="javascript" type="text/javascript" src="jquery-1.8.2.js"></script>
</head>
<body>
<script type='text/javascript'>
$( document ).ready( function() {
// Draw all slots
$('div.slot').each(function(i, d) {
console.log('slot found: ' + d.id);
// d.draw() does not work
draw();
});
});
function draw(behavior) {
console.log(behavior);
}
</script>
<div class="slot" id="slot1" data-behavior="drew 1">
</div>
<div class="slot" id="slot2" data-behavior="drew 2">
</div>
</body>
</html>
If you're looking to do something more complicated, you should consider building an object oriented javascript application, with each block's functionality derived from a class "slot".
https://developer.mozilla.org/en-US/docs/JavaScript/Introduction_to_Object-Oriented_JavaScript
You can call it like
HTML:
<div class="slot" id="slot1">Draw1</div>
<div class="slot" id="slot2">Draw2</div>
JS:
function draw()
{
console.log('Drawed! '+$(this).attr('id'));
}
$(document).ready( function() {
$('div.slot').each(function(i, d) {
console.log('slot found: ' + d.id);
draw.call($(this));
});
});
An Example.
​
The reason that is happening is because you keep overwriting the draw function. Why don't you have a script page where you hold an array of function pointers to the right function like so:
var array = (draw1, draw2, draw3, ...);
function draw1()
{
//do your thing on div1
}
...
function drawn()
{
//do your n thing on divn
}
Now for your first div you need to call draw1 which is located at index 1 of the array.
HTML:
<div id="draw1">
</div>
....
<div id="drawn">
What do ya think. Note sytax has not been tested.
<html>
<head>
<script>
$(document).ready(
function() {
$('#show').call(callfun());
});
</script>
</head>
<body>
<h:form>
<div id="show" align="center">
<script>
function callfun(){
var data = "hi";
alert(data);
}
</script></div>
</h:form>
</body>
</html>
I think it may work.
Problem
You keep overwriting window.draw() every time you redefine it. You either need to namespace each one (that is, attach it to an (otherwise empty) object), or to give each and every function a distinct name. There is no "div-scope" in Javascript ;)
Solution
You can name each function according to the div's id and call it dynamically using the object["methodName"]() syntax to call it.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
<script language="javascript" type="text/javascript" src="jquery-1.8.2.js"></script>
</head>
<body>
<script type='text/javascript'>
$( document ).ready( function() {
// Draw all slots
$('div.slot').each(function(i, d) {
console.log('slot found: ' + d.id);
// d.draw() does not work
window[d.id];
});
});
</script>
<div class="slot" id="slot1">
<script type='text/javascript'>
function slot1() {
console.log('Here we draw a circle');
};
</script>
</div>
<div class="slot" id="slot2">
<script type='text/javascript'>
function slot2() {
console.log('Here we do something totally different and draw a rectangle');
};
</script>
</div>
</body>
</html>
http://jsbin.com/mogeluzicu/1/edit?html,console
The easiest way I've found to go 'real OOP' in this case is to dispatch all on events on document level :
create a simple object and load this object in the main and the views like :
var events = {someCustomEventFromMain:'someCustomEventFromMain', someCustomEventFromView:'someCustomEventFromView'}
Now you can trigger events on document with jQuery like
$(document).trigger(events.someCustomEventFromMain, somedata);
And you can listen from any view or div or else
$(document).on(events.someCustomEventFromMain, function(__e, __data){
//___e is the event emitted from __e.target
//__data is the data object you wish to pass with the event
//do something when event occurs
});
So if every subscript listens to some event at document level, in your case 'drawEvent',that should do the trick. You can even pass a parameters in the data of the event, like 'circle'.
Hope this helps.

Access function from background page in Chrome extension

In my background (background.html) page I have the following js:
function capturePage(){
chrome.tabs.captureVisibleTab(null, function(img){
var screenshotUrl = img;
chrome.tabs.create({"url":"history.html"}, function(tab){
var t = tab;
var addImage = function(){
var view = chrome.extension.getViews()[0];
view.setImageUrl(screenshotUrl);
}
chrome.tabs.onUpdated.addListener(addImage);
});
});
}
chrome.browserAction.onClicked.addListener(capturePage);
and in history.html I have:
<html>
<head>
<title></title>
<script>
function setImageUrl(url){
document.getElementById("target").src = url;
}
</script>
</head>
<body>
<img id="target" src="" >
</body>
</html>
However, "view.setImageUrl(screenshotUrl)", in background.html, fails as it says the view has no such function. Just to be clear, I'm trying to access a function within history.html AND pass a parameter to it (screenshotUrl).
EDIT: re Serg's suggestion I replaced the var addImage function in background with the following:
var port = chrome.tabs.connect(tab.id,{name: "history_connect"});
port.postMessage({mType:"url",url:screenshotUrl});
Then added a listener on the history page... worked!
I haven't used getViews() before so I can't comment on that (what does console say when you dump chrome.extension.getViews() into it?), but here is couple workarounds:
Pass your url as get parameter during tab creation (history.html?url=<urlencoded_url>)
Use requests. chrome.extension.sendRequest({url:url}); in bkgd page and chrome.extension.onRequest.addListener() in history.html
Use "pull" instead of "push". In history.html you can use chrome.extension.getBackgroundPage().getMyUrl()
I would use the first solution as it is the easiest and fastest.

Categories

Resources