Other The javascript horrortrip

Hi folks,
I admit, I never liked javascript, so I avoided it as much as possible. For a couple of reasons - but here is not the place to discuss tastes. Rather, I am just looking for a way to understand what is actually going on in those code snippets.

Somehow accidentally I figured a little piece of code, which does actually work and do what I intended (and appears to be quite helpful), although I don't fully understand why (this is an example, there is a couple more of them):
Code:
var startupFunc242 = function() {
    var list; var i;
    list = document.querySelectorAll('[data-href242]');
    for (i = 0; i < list.length; i++) {
        list[i].onclick = function() {
            window.location.href=this.dataset.href242;
        };
    };
};
window.onload = startupFunc242;

Then, when adding an obtained package that would need the same startup, I noticed that onload does not stack. So, for concistency, I started to change all to addEventListener. And that did not work:

Code:
  list = document.querySelectorAll('[data-href242]');
  for (i = 0; i < list.length; i++) {
    list[i].addEventListener('click', () => {
      window.location.href=this.dataset.href242;
    });
  };

TypeError: _this.dataset is undefined

So I started to search for a way to get rid of this - I don't like it anyway because I never know what this is actually supposed to be. And the fun increased: after a lot of unsuccessful tries I came across that one (which doesn't work either):

Code:
  list = document.querySelectorAll('[data-href242]');
  for (i = 0; i < list.length; i++) {
    var s = list[i].dataset.href242;
    alert(s);
    list[i].addEventListener('click', function() {
      alert(s);
      window.location.href=s;
    });
  };

At the first alert, the proper string is present in the variable, but at the second it contains something like [object HTMLInputElement]. So it is not that the variable wouldn't be accessible, it is the content that isn't visible!
That bug of a language seems to work 100% by-reference, and there is no way around (at least I found none, and I tried all the usual tricks). Probably for 'security' reasons.

But then, the total surprize, writing it this way, it suddenly works again (and no 'security' anymore):

Code:
  document.querySelectorAll('[data-href242]').forEach((i) => {
    i.addEventListener('click', () => {
      window.location.href=i.dataset.href242;
    });
  });

So what is going on here??
 
My impression is you want to simulate <a href="..."> but I don't understand why you create such a complicated design for such a simple purpose. :/
What do you want to achieve? I could provide you with some explanations, and perhaps a simpler implementation.
 
  • Thanks
Reactions: PMc
If I recall, "this" changed inside the function because it now refers to the inner function rather than the outer context.

Rather than using the () => stuff, what about this:

Code:
list = document.querySelectorAll('[data-href242]');
for (i = 0; i < list.length; i++) {
  list[i].addEventListener('click', function() {
    window.location.href=this.dataset.href242;
  });
};

In any case, glad you seem to have found a solution (again, presumably because that new 's' variable was not being changed unlike the "this"). So the above is more out of interest.
 
So I started to search for a way to get rid of this - I don't like it anyway because I never know what this is actually supposed to be. And the fun increased: after a lot of unsuccessful tries I came across that one (which doesn't work either):

Code:
  list = document.querySelectorAll('[data-href242]');
  for (i = 0; i < list.length; i++) {
    var s = list[i].dataset.href242;
    alert(s);
    list[i].addEventListener('click', function() {
      alert(s);
      window.location.href=s;
    });
  };

At the first alert, the proper string is present in the variable, but at the second it contains something like [object HTMLInputElement]. So it is not that the variable wouldn't be accessible, it is the content that isn't visible!

What happens if you change var to let?
 
  • Thanks
Reactions: PMc
My impression is you want to simulate <a href="..."> but I don't understand why you create such a complicated design for such a simple purpose. :/
What do you want to achieve? I could provide you with some explanations, and perhaps a simpler implementation.

Yeah, that would be great! :) I don't really know how such things should be done properly, I merely found out that it somehow works (and is quite useful). What I'm doing with it:
  • get "clickable surfaces" - e.g. an entire table row
  • write links as <button type="button"> to get the same style for all (e.g. "go back" and "submit" buttons in a form)
Then I have another, very similar one that does not href but click() elsewhere, to create seemingly nested forms where needed (e.g. an upload button inside another form, that shall work with AJAX - e.g. an editor with a sidebar of thumbnails, where an additional graphic should be uploadable while resume editing).
It's basically a "wiring box" for things that by-design want to be somewere, but by-semantic need to be somewhere else.
 
What happens if you change var to let?

Yes that helps.
Probably my fault - I was thinking when I declare a variable inside a block then it should be local to that block. Then I copied the thing, with the same names, for another similar purpose.
Now I tried to reproduce it (by changing only one occurence back), and couldn't. Finally I restored from backup the exact files as from in the morning (so the restore is now also tested).

So, var is only local inside a function, but not inside a {} block? But then it also doesn't report a duplicate declare. :(
 
  • get "clickable surfaces" - e.g. an entire table row
  • write links as <button type="button"> to get the same style for all (e.g. "go back" and "submit" buttons in a form)
Clickable surface can be achieve with a a href around in HTML 5.
Styles have to be handle with css and class.

You could search «progressive enhancement» to see how you could reduce you javascript code to only necessary function and have a clean backup in HTML.
 
Clickable surface can be achieve with a a href around in HTML 5.

Yes, certainly it can be achieved. But not so certainly will I create twohundredandfiftythousand a_href around table cells (or other things) and look after their individual proper handling.

Styles have to be handle with css and class.

That's a nice idea, but practically impossible:
In a form, the submit must be a button, while a "go back" link (or any other link) cannot be a button (because buttons offer only submit, reset and nothing). And with CSS and classes it is not possible to get a button and a link with the same style. End of story.

You could search «progressive enhancement» to see how you could reduce you javascript code to only necessary function and have a clean backup in HTML.

I fear now that's too late. I have always tried to avoid that plague of a language. But recently my building piece, RoR, has brought along another beauty, called webpack, and I had to confront that issue. Now at least I want some benefit from it.
 
... What I'm doing with it:
  • get "clickable surfaces" - e.g. an entire table row
...
IMHO, the cleanest way to clickable table rows or cells (or other elements) is by adding event listeners like so:
HTML:
<!DOCTYPE html><HTML><HEAD><TITLE>Example</TITLE>
<META http-equiv="Content-Type" content="text/html; charset=utf-8">
<STYLE>table {width:38%; margin:25px auto; border-collapse:collapse; border:2px solid black; text-align:center;} td,th {border:1px solid black;}</STYLE>
<SCRIPT>"use strict";
   function clicked()
   {
      alert("clicked cell at: " + this.cellIndex + "," + this.parentNode.rowIndex);
   }

   function addTableCellListeners()
   {
      var table = document.getElementById("example");
      if (table != null)
         for (var i = 0; i < table.rows.length; i++)
            for (var j = 0; j < table.rows[i].cells.length; j++)
               table.rows[i].cells[j].addEventListener("click", clicked);
   }
</SCRIPT></HEAD>
<BODY onload="addTableCellListeners();">
   <TABLE id="example">
      <TR><TD>0,0</TD><TD>1,0</TD><TD>2,0</TD><TD>3,0</TD></TR>
      <TR><TD>0,1</TD><TD>1,1</TD><TD>2,1</TD><TD>3,1</TD></TR>
      <TR><TD>0,2</TD><TD>1,2</TD><TD>2,2</TD><TD>3,2</TD></TR>
      <TR><TD>0,3</TD><TD>1,3</TD><TD>2,3</TD><TD>3,3</TD></TR>
   </TABLE>
</BODY></HTML>

Instead of the alert() we would program something more useful, of course.
 
You should use 'let' and 'const' instead of 'var' because their behavior is more predictable, but their purpose is the same, so don't expect miracles.

Don't use 'this' unless you create ES6 classes, or use it only when its meaning is well defined (e.g. in an HTML event attribute). JavaScript is NOT an object-oriented language, it just sometimes looks like object-oriented, but the illusion disappears when you have to use bind().

To make table rows clickable, you can handle the 'click' event on all <td> of the row, but tables are difficult to use for anything else than displaying tabular data. Maybe you'd like to use <div> with display = flex or grid instead, depending on your need.

<button> does not submit the form unless you define its type to 'submit', so your idea of using them for links is correct (just handle their 'click' event, e.g. <button onclick="history.go(-1)">Back</button>).

However, you should think about navigation on the application level - or at least on the feature level - in order to provide a consistent user experience.
 
In a form, the submit must be a button, while a "go back" link (or any other link) cannot be a button (because buttons offer only submit, reset and nothing). And with CSS and classes it is not possible to get a button and a link with the same style. End of story.
It is possible. Bulma as an exemple.

If this can help you :
To add the click event in a NodeList, you can do this :
[].forEach.call(elements, (element)=>{ element.addEventListener('click', (event)=>...
(Sorry for the use of CMD, The code formatting here refuse []).

But you can add a instead of click event, you have more control about the area that can be clicked.
 
I admit, I never liked javascript, so I avoided it as much as possible.
I similarly dislike JavaScript, finding myself in agreement with Microsoft, who disliked it enough to wrap it in their own language, TypeScript. Unfortunately, however much I think it sucks, it is the way of the tech world at the moment. Having been hands-off web development for a few years I found an article depressingly entitled Modern JavaScript Explained For Dinosaurs very helpful for engaging with current tools. Having explored the joys of Vue.js, which at least served to dull some of the pain of developing in JavaScript, my next adventure will be in Elm, which compiles to JavaScript from a Haskell-like language. As a big fan of Haskell, I'm hopeful, even if the concept of compiling to an interpreted language feels rather depressing. For anything performance intensive that doesn't involve a web front-end, it's possible to compile various languages to WebAssembly, a modern innovation in web development that took until 2017 to bring back the architecture of Java applets circa 1995. Did someone say "Dinosaur"? Perhaps I'm growing hard of hearing as the years go by...
 
Unfortunately, however much I think it sucks, it is the way of the tech world at the moment.

Yes, though as far as disposable scripting languages go (Lua, Python, Perl, etc), it is "good enough". I tend to make "objects" in the same way I do in Perl (Slow and steady).

Code:
function MyObject()
{
  var self = {};

  self.someVar = 9;

  self.someFunc = function()
  {
    return self.someVar;
  };

  return self;
}

var mo = MyObject();
mo.someFunc();

Notice, I avoid "this" in all cases. Its rules are not worth dealing with ;)

Kind of wasteful because each object duplicates the functions but if we were truly trying to be efficient, we would obviously be using C or C++ instead.
 
No, JavaScript "objects" use prototypes, they don't duplicate functions.
This is what makes "polyfills" possible.
You almost certainly know more than me how this works. Does it not create something like a closure?
I.e in the "someFunc" function, it refers to "self" so does some sort of container not get created underneath to keep this relationship to differentiate between mo1.someObject's self and mo2.someObject's self.

So if I did:

Code:
var mo1 = MyObject();
var mo2 = MyObject();

Is the someFunc is not being duplicated as some sort of function object using my approach? Would it would be just as efficient as:

Code:
function someFunc()
{
  // ...
}

var mo1 = {};
mo1.someFunc = someFunc;
var mo2 = {};
mo2.someFunc = someFunc;

I honestly don't know. Trying to find any "correct" information about javascript normally just ends up with some kid telling me to use JQuery XD

Some info on this here. It is Lua (where I first found this technique). I assumed Lua and Javascript used a similar mechanism and shared the same disadvantage with this approach.
(Paragraph containing "a disadvantage for efficiency reasons")

("Instances are slower to create and use more memory")
 
I.e in the "someFunc" function, it refers to "self" so does some sort of container not get created underneath to keep this relationship to differentiate between mo1.someObject's self and mo2.someObject's self.

It's a closure, so it "closes over" self.

Code:
var mo1 = MyObject();
var mo2 = MyObject();

The someFunc is not being duplicated as some sort of function object using my approach?

That will give you 2 closures (function objects or whatever terminology you prefer).
 
That will give you 2 closures (function objects or whatever terminology you prefer).

Just to check. If the MyObject also had a self.someFunc2 = function() {..}; inside it then I would have ended up with 4 closures?

If so then yes, that's what I thought (and the same in Lua). This likely wastes a bit more memory than other solutions. I have never really built any large scale software in JS or Lua so I don't know quite how critical this is. I find the JS prototype stuff and Lua metatable stuff a bit ugly as alternatives. This closure approach also works pretty well with some level inheritance.

Where I know this would be pretty bad is in something like a maths library. Each time I create a i.e Vec3, it creates closures for add, div, mul, sub, etc. This would be very expensive.
 
If the MyObject also had a self.someFunc2 = function() {..}; inside it then I would have ended up with 4 closures?

Yep.

I find the JS prototype stuff and Lua metatable stuff a bit ugly as alternatives.

Pure prototype code doesn't look all that confusing:
Code:
let foo = { what: 'foo', name: function() { return this.what; } };
let bar = { what: 'bar' };
bar.__proto__ = foo;
console.log(bar.name()); // prints "bar"

(Although JS prototypes are not so much a conscious design choice, but rather a minimally viable inheritance implementation. Classes take more design and implementation work and JS was quickly cobbled together.)

The fun part is that direct __proto__ manipulation was always discouraged in preference of the wtf-level constructor function syntax. At least now JS has classes, those are relatively straightforward to use.
 
In a form, the submit must be a button, while a "go back" link (or any other link) cannot be a button (because buttons offer only submit, reset and nothing). And with CSS and classes it is not possible to get a button and a link with the same style.
It is possible. Bulma as an exemple.
Okay, please tell me how.

I have searched for this quite extensively, and did not find a correct solution. CSS libraries like Bootstrap4 (and Bulma is probably something similar) have ways to fix the graphical appearance, but not the typesetting.

Typesetting in a button is centered, and that is what we expect from a button. A link, to the contrary, has normal running-text typesetting, that starts in the topmost left position, and then wraps around whenever a line is full. A button does by default no wraparound, but hangover left and right.
There is a CSS parameter to make the button wraparound, and there is one to center the text in the link. What you will then get, is this:

Code:
    Link             Button

--------------------------------
|      Go      |               |
|     Back     |               |
|              |     Submit    |
|              |      this     |
|              |      Form     |
|              |               |
|              |               |
--------------------------------

The text in the link can be centered horizontally, but not vertically.

Proposed solutions for this issue, as found on the web:
  • adjust the line-height for the text in the link, so it gets centered vertically.
    That obviousely doesn't work with wraparound, and worse, that line-height would need to be calculated for each possible text (think: languages) individually.
  • adjust some kind of margin parameter to get the text centered vertically. This also does not work with scaling (aka "responsive") layouts, where the height of the box may change when resizing the window.
  • do not center the text within the link, but center the link within the box. That does work, but: only the link is clickable, the box is not! That way most of the box will not be clickable.
So, if You have found a working solution without JS, please tell me.
 
I have searched for this quite extensively, and did not find a correct solution.

A small hint is that tables vertically center items. However many web developers for some reason will shout at you for using tables. So a better solution is to use tables under a different name "flexbox".

Basically do what you would do for Gtk+ and have an expanding element above and below it.

Code:
--------------------------
|
| (expanding div)
|
--------------------------
|  Go Back
--------------------------
|
| (expanding div)
|
--------------------------

You can make things expand using the flex-grow property. Check out: https://css-tricks.com/snippets/css/a-guide-to-flexbox/

Code:
<div style="background: red; height: 200px; display: flex; flex-direction: column">
  <div style="flex-grow: 1"></div>
  <div>
  Go Back
  </div>
  <div style="flex-grow: 1"></div>
</div>

example:
 
A small hint is that tables vertically center items. However many web developers for some reason will shout at you for using tables. So a better solution is to use tables under a different name "flexbox".

Yes, been there, tried that.

Basically do what you would do for Gtk+ and have an expanding element above and below it.

Code:
--------------------------
|
| (expanding div)
|
--------------------------
|  Go Back
--------------------------
|
| (expanding div)
|
--------------------------

Yes. And this is where this concern would apply:
  • do not center the text within the link, but center the link within the box. That does work, but: only the link is clickable, the box is not! That way most of the box will not be clickable.

In this case, the "expanding div" regions is not clickable.
 
display: inline-block?
That way you can make a piece of running-text into a block (that honors some given height). So one could then tell it to make the height: 100%; - and then the link will nicely occupy the box and all is clickable - but the text will still be rendered at the topmost left position within that new box:
 
I similarly dislike JavaScript, finding myself in agreement with Microsoft, who disliked it enough to wrap it in their own language, TypeScript. Unfortunately, however much I think it sucks, it is the way of the tech world at the moment. Having been hands-off web development for a few years I found an article depressingly entitled Modern JavaScript Explained For Dinosaurs very helpful for engaging with current tools.

You should get alerted about that. With the editor I'm using (CKE5), the docs don't get tired stressing that it supports modern JS. This seems to be kind of a meme, and may not mean what is appears to say. What they are doing doesn't look very modern to me anyway, and also, "modern" may not even be a positive feature. In fact, the word "modern" derives from the fashion business (german: Mode=fashion), and what it actually means is that you can throw away that stuff after half a year. That might be appropriate for girls' dresses, but certainly not for technology. Unix never claimed to be modern, but it works in the way it was designed for half a century now and maybe it will work forever. The "modern" web development has still to prove that it will survive as much as ten years - and I doubt it will.
 
Couple of comments from what I've seen but I'm late to this thread and it's changing course.

Tables should not be used for layout because tables are structure of the document and layout should be confined to CSS. You can't easily, if at all, move content from one data element, such as a <td> which is why people used to "slice" images back in the day. The guy who first thought of using tables for layout wrote an article, "The web is ruined and I ruined it!", lamenting his method.

One should be aware that links, typically created with anchor elements <a>, are to be used when referencing other pages, web sites or content within the same document (web page). Buttons are to be used to initiate an action, such as submitting a form or changing something.

One thing that needs to be learned is the "box model" in CSS. Once you get that, the rest is relatively easy...sort of.

I would also get used to not worrying about which IDE or editor to use at first. Just create a file on your computer named index.html (I even use 1.html, 2.html, etc.) for quick and dirty testing of any ideas I might want to try. I just open that in the browser. Of course, I have a server to put that up on, too.

Learn the fundamentals. HTML, CSS and JavaScript. Don't jump into Typescript or React or any of that other stuff huge companies use for themselves and now everyone thinks they have to use it on their static web site now.

The best resources for HTML and CSS specifications and MDN. You won't need anything else. Example: https://mdn.io/box model
 
Back
Top