37

I'm trying to write entries to the console that would contain links to trigger javascript functions upon clicking on them:

console.log("javascript:alert('Hello World');");
console.log("<a href=\"javascript:alert('Hello World');\"/>test</a>");
console.log("[DisplayObject 'Hello World' <a href=\"javascript:alert('Hello World');\">reveal</a>]");

All these attempts fail.

screenshot

Is there any way to print it similarly to an "http://..." link, like so...

example

... only, have the text linked to a javascript statement or function instead?

The reason I'm doing this is to be able to quickly reveal which items on the screen are tied to a specific log entry (example: Making a CreateJS sprite scale-up when the log entry is clicked on).

Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
chamberlainpi
  • 4,854
  • 8
  • 32
  • 63

3 Answers3

28

The Google Chrome console, like many other browser's developer tools consoles, automatically parses any URL into a link to that exact page. This is the only way of obtaining such URLs, and, unfortunately, you cannot actually log "custom URLs".

This means that the following logs will be turned into a clickable link automatically:

console.log('http://example.com');
console.log('www.example.com');
console.log('ftp://mysite.com');
console.log('chrome://history')
console.log('chrome-extension://abcdefg...');

but, on the contrary, the following ones won't:

console.log('example.com');
console.log('<a href="http://www.example.com">Link</a>');
console.log('javascript:doSomething(someValue);');
Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
  • Thanks, I took a different direction after reading your answer; I'm basically rolling my own popup (child window launched by the game/app) that I can fill with any kind of logging, and just tie the JS functions directly to each DIV log entries' click event. Might be more flexible for displaying other formatted content too. – chamberlainpi Apr 17 '15 at 10:31
  • @bigp I would be glad to see that ! Is it open-source ? – lud Feb 02 '16 at 15:08
  • Sorry @niahoo, I don't remember where I stored those files anymore (this was done at my last job back in like Spring 2015). – chamberlainpi Feb 02 '16 at 15:30
  • @bigp haha ok no problem :) – lud Feb 02 '16 at 15:40
19

It's been four years since OP asked this, but they can accomplish their stated goal as follows:

Create a getter that has a side-effect. (The side effect can be to directly animate your thing.)

class YourThing {        
    get __doAnythingYouWant() {
        // ...like console.log('my thing:', this);
    }
}

Alternatively:

var yourObject = {
    get __doAnythingYouWant() {
        // ...like an animation in DOM... this.myDomElement.style...
    }
};

How to use it:

console.log(yourObject);
> {
      get __doAnythingYouWant: (...)  <-- click here!
  }
> "you clicked the dots!"

The downside is you can only invoke the getter once. You could maybe get around this by deleting it and redefining it from within itself, with every invocation. You could make that less hackish by creating some kind of getter-defining function with Object.defineProperties, and some wrapper that takes a regular function and returns a function that does what it normally does, then redefines the getter. Clicking on it once was good enough for me, but if you wanted to you'd do it like this:

function addDebuggerButton(obj, f) {
    var buttonName = `__${f.name}`;
    Object.defineProperty(obj, buttonName, {
        get: function() {
            f.apply(this, arguments);
            delete this[buttonName];
            addDebuggerButton(obj, f);
        },
        configurable: true
    });
    
    return obj;
}

It's important to set the configurable property, which just lets you redefine it after defining it. It works:

let numTimesPressed = 0;
addDebuggerButton({a:1,b:2}, function myButton() {
    this.c = Math.random();
    console.log(`you pressed the button ${++numTimesPressed} times!`, this);
});
> {a: 1, b: 2}
      a: 1
      b: 2
      __myButton: (...)       <-- click here!
      get __myButton: ƒ ()
      __proto__: Object
> you pressed the button! {a: 1, b: 2, c: 0.27574428165568676}
                              a: 1
                              b: 2
                              c: 0.27574428165568676
                              __myButton: (...)      <-- click here again
                              get __myButton: ƒ ()
                              __proto__: Object
> you pressed the button! {a: 1, b: 2, c: 0.43171172657344337}

You can modify this as appropriate to work with classes.

If you wanted clicking on different links to have their own different timelines of state (rather than shared state like numTimesPressed above, which is a global, or somewhat more global, in scope; i.e. such state is shared between different objects), then you could make the function return a callback containing the rest of your computation. You'd have to modify addDebuggerButton accordingly (let callback = f.apply(this, arguments);, addDebuggerButton(obj, callback)).


The other horribly hackish way to do this is to write a url like http://127.0.0.1:9999/myLocalWebserver/horribleWorkaround?{"metadata":"etc"}. The URL would be interpreted by your local webserver you're testing your app out of, and would initiate some kind of push mechanism (or queue onto a poll mechanism), that would propagate to the webpage live. This would flash open a new tab, in which you could display a dummy webpage which auto-closes itself. You could also maybe make this entirely client-side with a webworker that intercepts requests.

... Of course, it would probably be more elegant to compile Chromium yourself with a custom patch...


The third way, since chrome-extension:// urls are allowed, is perhaps to write an extension that converts certain clicks into javascript. There may perhaps be security implications (to be safe, you would not let this run except on whitelisted pages, but even then there are security implications I have not thought of since I'm not entirely familiar with that area).

ninjagecko
  • 88,546
  • 24
  • 137
  • 145
  • As my friend said, "This is clever AF!". Just to add, you can also open windows as a side effect via `window.open()`, turning the `...` into a kind of link. – Stan James Nov 25 '19 at 01:46
  • 1
    The unfortunate part is that currently these objects are printed as collapsed `Object {}` which you have to click before showing any other property. – fregante Mar 13 '22 at 06:41
-1

If it still comes up for somebody:

You can more-or-less log "custom URLs":

console.log('There is a good link: %o', 'http://stackoverflow.com');

Check this CheatSheet

Gabor
  • 445
  • 1
  • 4
  • 7
  • 3
    I don't see how is this "more-or-less custom", since it's the same as `console.log('There is a good link: "http://stackoverflow.com"');` – Marco Bonelli Jun 05 '18 at 12:06
  • 1
    You are right. With current versions of Chrome indeed it's the same. But I am sure back that time I wrote the comment only the pure links, and the `%o` parameters were turned into links. – Gabor Jul 03 '18 at 08:12