查找Chrome控制台是否已打开

200

我正在使用这个小脚本来判断Firebug是否已打开:

if (window.console && window.console.firebug) {
    //is open
};

它很有效。现在我搜索了半个小时,想找到一种检测Google Chrome内置的Web开发人员控制台是否打开的方法,但是我找不到任何线索。

这个:

if (window.console && window.console.chrome) {
    //is open
};

无法正常工作。

编辑:

所以看起来不可能检测到Chrome控制台是否打开。但是,有一个"hack"可以使用,但存在一些缺点:

  • 当控制台未停靠时将不起作用
  • 当控制台在页面加载时打开时将不起作用

因此,我暂时选择Unsigned的答案,但如果有人想出了一个绝妙的想法,他仍然可以回答并且我将更改选定的答案!谢谢!


答案中的解决方案似乎有效,但仅当控制台被停靠时才有效。如果控制台在页面加载时已经打开,则它不起作用,而Firebug脚本则没有这些问题,似乎总是有效。但是我现在可以接受这种情况!非常感谢@pimvdb!!无论如何,我将保持问题的开放状态,以便找到类似于Firebug脚本的始终有效的方法。 - r0skar
我一直在尝试一些方法,比如抛出一个错误并查看是否获取了.message(当调试器打开时会看到消息),但不幸的是,即使调试器没有打开,这种情况也会发生。如果有的话,我想知道一个解决方法... - pimvdb
2
问题是为什么你需要这样做?如果你想要防止调试,还有其他可以替代的调试工具。 - Spudley
5
@Spudley 这个问题并不涉及我为什么需要它,我也不想开始解释。我知道没有办法阻止某人进行调试,但这不是我所尝试的。我只是想找出一种方法来判断控制台是否打开。就这样 :) - r0skar
1
JFYI,console.profiles方法最近已从控制台API中删除。 http://src.chromium.org/viewvc/blink?view=revision&revision=151136 - loislo
您可以访问https://collabland.la/进行查看。 当您在Chrome中打开开发者工具时,它会自动进入调试模式,这意味着它检测到Chrome控制台已打开。 如果可能的话,我想要一个解释为什么会发生这种情况以及如何做或保护它。 - toptecshare
24个回答

143

对于历史背景,以下是之前的回答。

调试器(2022)

虽然不是绝对可靠的,但另一个回答中的基于调试器的方法似乎仍然有效。 参考链接

requestAnimationFrame (晚2019年)

目前,Muhammad Umer的方法在Chrome 78上有效,并具有检测关闭和打开事件的附加优势。

函数toString (2019年)

感谢Overcl9ck在此答案中的评论。将正则表达式/./替换为空函数对象仍然有效。

var devtools = function() {};
devtools.toString = function() {
  if (!this.opened) {
    alert("Opened");
  }
  this.opened = true;
}

console.log('%c', devtools);
// devtools.opened will become true if/when the console is opened

regex toString (2017-2018)

这是一个解决方案,如果原始提问者已不在场并且此解答仍被接受,可以添加该解决方案以提高其可见性。感谢Antonin Hildebrand评论中的贡献和zswang回答。该解决方案利用了只有在控制台打开时才会调用日志对象上的toString()方法的事实。

var devtools = /./;
devtools.toString = function() {
  if (!this.opened) {
    alert("Opened");
  }
  this.opened = true;
}

console.log('%c', devtools);
// devtools.opened will become true if/when the console is opened

控制台剖析 (2013)

更新:console.profiles 已从Chrome中删除。此解决方案不再可用。

感谢Paul Irish指出来自Discover DevTools的解决方案,使用剖析器:

function isInspectOpen() {
  console.profile();
  console.profileEnd();
  if (console.clear) {
    console.clear();
  }
  return console.profiles.length > 0;
}
function showIfInspectIsOpen() {
  alert(isInspectOpen());
}
<button onClick="showIfInspectIsOpen()">Is it open?</button>

window.innerHeight (2011)

这个选项可以检测到页面加载后打开的停靠式检查器,但无法检测到未停靠的检查器,或者在页面加载时检查器已经打开。还存在一些误报的可能性。

window.onresize = function() {
  if ((window.outerHeight - window.innerHeight) > 100) {
    alert('Docked inspector was opened');
  }
}


2
在isInspectOpen()中出现TypeError: Cannot read property 'length' of undefined。 - sandeep
2
有一个全新的最佳方法(来源:@zswang):https://dev59.com/nmsz5IYBdhLWcg3wlY0n#30638226 - Vicky Chijwani
4
“toString(2017)”的解决方法在Chrome浏览器中不起作用。 - Richard Chan
2
toString 在 Chrome 中似乎已经被修复了。编辑。实际上,如果您使用 function() {} 而不是正则表达式,它就可以工作。 - Overcl9ck
2
已确认在Chrome v80.0.3987.149版本中仍未能正常工作。 - BananaAcid
显示剩余13条评论

127

Chrome 65+(2018年)

    r = /./
    r.toString = function () {
        document.title = '1'
    }
    console.log('%c', r);

示例: https://jsbin.com/cecuzeb/edit?output(更新于2018-03-16)

包: https://github.com/zswang/jdetects


在 Chrome 开发者工具中打印“Element”时,将获取其 ID。

    var checkStatus;
    
    var element = document.createElement('any');
    element.__defineGetter__('id', function() {
        checkStatus = 'on';
    });
    
    setInterval(function() {
        checkStatus = 'off';
        console.log(element);
        console.clear();
    }, 1000);

另一种版本(来自评论)

var element = new Image();
Object.defineProperty(element, 'id', {
  get: function () {
    /* TODO */
    alert('囧');
  }
});
console.log('%cHello', element);

打印普通变量:

    var r = /./;
    r.toString = function() {
      document.title = 'on';
    };
    console.log(r);


4
好的回答。补充一点,MDN表示__defineGetter__已过时,所以我改用了Object.defineProperty(element, 'id', {get:function() {checkStatus='on';}});,仍然有效。 - denikov
5
此外,控制台将在打开时“读取”元素,因此您可以只打印一次并等待getter函数执行而不是设置setInterval - xpy
9
基于这一发现,我能够找到更少侵入性的方法。DevTools在将函数打印到控制台时会调用toString()。因此,可以通过重写带有toString()方法的自定义函数对象并返回空字符串来打印它。此外,您还可以使用控制台格式化字符串%c并设置颜色:transparent,以确保潜在的打印文本以不可见的方式打印。我在这里使用了这种技术:https://github.com/binaryage/cljs-devtools/blob/2e0615e7e037945db8ff39581de6cd367d9388d4/src/lib/devtools/util.cljs#L108 - Antonin Hildebrand
4
2017年了。Chrome仍会在你没有打开控制台的情况下向其写入内容。而你的黑客手段已经不再奏效。 - vothaison
2
Chrome canary现在阻止第一种方法生效(https://github.com/zswang/jdetects/issues/3) - Sam Denty
显示剩余5条评论

51
非常可靠的黑客。 基本上在属性上设置一个 getter ,并将其记录在控制台中。很明显只有当控制台打开时才会访问该内容。 https://jsfiddle.net/gcdfs3oo/44/

var checkStatus;
var indicator = document.querySelector('#devtool-status');

var element = new Image();
Object.defineProperty(element, 'id', {
  get: function() {
    checkStatus='on';
    throw new Error("Dev tools checker");
  }
});

requestAnimationFrame(function check() {
  checkStatus = 'off';
  console.dir(element);
  indicator.className  = checkStatus;
  requestAnimationFrame(check);
});
.on{
  color:limegreen;
}

.off{
  color:red;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.7.1/css/all.css" integrity="sha256-DVK12s61Wqwmj3XI0zZ9MFFmnNH8puF/eRHTB4ftKwk=" crossorigin="anonymous" />

<p>
  <ul>
    <li>
      dev toolbar open: icon is <span class="on">green</span>
    </li>
    <li>
      dev toolbar closed: icon is <span class="off">red</span>
    </li>
  </ul>
</p>
<div id="devtool-status"><i class="fas fa-7x fa-power-off"></i></div>
<br/>
<p><b>Now press F12 to see if this works for your browser!</b></p>


6
throw new Error("Dev tools checker"); 的作用是什么?因为没有它也能正常运行。 - Legends
2
这似乎会在控制台中产生垃圾信息(当打开时)?我猜几天后开始会占用大量内存 :) - pythonator
10
Chrome Canary 89.0.4373.0 不再可用 ❎ - Dickeylth
1
@Legends 如果你尝试在代码中自己访问 element.id,它会抛出一个错误,以便你知道你在使用它时是错误的。console.log(element.id) 不起作用,因为你正在自己访问 id,而目标是让控制台访问该属性。console.dir 将自动访问所提供参数的所有属性,并且它将抑制/忽略该错误。这就是为什么这是一个聪明的技巧,因为它强制控制台进行属性访问。现在,如果我们只能想出一种不垃圾填满控制台的方法就好了。 - Francisc0
7
Chrome 90 (stable) 中此功能已不再可用。 - eoleary
显示剩余5条评论

29

我创建了devtools-detect,它可以检测DevTools是否打开:

console.log('is DevTools open?', window.devtools.open);

你还可以监听一个事件:

window.addEventListener('devtoolschange', function (e) {
    console.log('is DevTools open?', e.detail.open);
});

当DevTools未停靠时,它无法工作。但是,它能够与Chrome / Safari / Firefox的DevTools和Firebug一起使用。


@barbushin,我认为你的开放式开发工具停靠了,它无法检测到独立窗口。 - Mithril
2
很遗憾,它在Chrome上停止工作了。 https://github.com/sindresorhus/devtools-detect/issues/40 - laike9m

19
console.log(Object.defineProperties(new Error, {
  message: {get() {alert('Chrome/Firefox')}},
  toString: {value() {(new Error).stack.includes('toString@')&&alert('Safari')}}
}));

演示:https://jsbin.com/cateqeyono/edit?html,output


不错的发现!有没有办法在控制台关闭后重新加载它自己/使其轮询而不会在控制台打开时重复触发?一种方法是使用高频setTimeout,每次触发时更新时间戳,然后仅在先前的时间戳值未定义或足够接近当前时间时执行回调(这里是alert())。时间戳可以通过IIFE私有化。 setTimeout的缺点是控制台垃圾邮件。 - starball
在设备工具栏被切换开启(Chrome)时无法工作。 - Asalan
@Asalan,如何重现问题?当我创建一个基本的HTML文件,并在<script>中添加此代码时,在没有打开开发者工具的情况下打开页面,然后已经设置了开发者工具以保持设备工具栏打开,这对我有效。 - starball
@Asalan 请问如何重现这个问题?当我创建一个基本的HTML文件,并在<script>标签中添加这段代码后,关闭开发者工具并打开页面,然后再次打开开发者工具并确保之前已经设置了开发者工具以保持设备工具栏打开,这样对我来说是有效的。 - starball
@starball 打开页面时关闭开发者工具 > 打开开发者工具 > 设备工具栏打开(375像素)> 关闭设备工具栏后关闭开发者工具 > 重新加载页面 > 然后打开开发者工具。 - Asalan

18

------ 更新:------

这是一个旧问题,有许多很好的答案在一段时间内都有效。截至2022年9月5日,目前最佳答案是由@starball提供的https://dev59.com/nmsz5IYBdhLWcg3wlY0n#68494829

顺便说一下,自从我发布这个答案以来,它仍然有效,只是有点难以保证始终准确。在我的演示中,点击“手动基准测试”链接,无论控制台是关闭还是打开,都可以看到我所指的差异总是很大。

----------------------

我找到了一种方法来判断Chrome控制台是否打开。 这仍然是一种技巧,但它更准确,并且无论控制台是否被拆开都可以工作。
基本上,当控制台关闭时,运行此代码大约需要100微秒,而当控制台打开时,需要大约两倍的时间,即200微秒。
console.log(1);
console.clear();

(1毫秒 = 1000微秒)
我在这里写了更多关于它的内容。
演示在这里。

3
这是一个非常错误的解决方案。谷歌一下 -> “竞争危害”。电脑速度快或慢会有什么影响? - 18C
2
这里与“竞争条件”无关。控制台打开时总是存在相对缓慢的情况。 - guya
2
相对滞慢,但不总是 100 或 200 毫秒。因此会出现竞态条件问题。顺便说一下,如果您同时玩游戏,这种“解决方案”将返回错误的正结果。 - 18C

15

解决方案似乎有几类常见的:

  • 依赖于检测屏幕调试工具出现时的大小调整(当调试工具/控制台作为独立窗口打开时不起作用)
  • 截取某些可以打开调试工具/控制台的用户操作,如右键菜单、F12、Ctrl+Shift+C等。这不能覆盖在浏览器chrome中的UI机制,这些机制无法被页面检测到。
  • 将一些内容记录到控制台,并依赖于特定于浏览器的行为进行懒惰、花哨的打印。历史上,这些似乎并不是非常可靠,但它们简单易用。如果你希望它们在同一浏览会话中反复工作,可能需要承受一定程度的控制台垃圾信息。
  • 使用debugger语句的时间启发式方法。棘手之处在于找到一种方式,使计时器不会被事件循环队列中的长时间运行的任务所混淆,并且debugger语句会暂停它所在线程的执行。还有一个挑战是,普通的debugger语句可以被用户逐个禁用或全部禁用。
以下是我解决debugger方法中特定问题的方案。即避免主线程在启发式计时器之间运行长任务时出现误报,避免debugger语句阻塞主线程,并防止禁用debugger语句。注意:我认为没有办法防止用户禁用所有调试器断点,这可能是最好的做法。
工作原理如下: Chrome浏览器开放devtools并且一个线程遇到debugging语句时,就会进入调试模式。
1. 主线程向WebWorker线程发送消息。 2. Worker线程回复一个开头心跳。 3. 主线程将开始一个计时器以期望收到闭合心跳。 4. Worker线程的消息处理器遇到一个debugger语句(可选地包装在eval语句中以防止用户禁用它)。 5. 如果devtools关闭,则worker立即向主线程发送确认,主线程将得出结论:devtools已关闭。如果打开了开发工具,Worker将进入调试会话,主线程将注意到Worker没有响应得足够快,从而得出结论:调试器必须是开启的。主线程不会被Worker的调试会话阻塞,但它的超时响应将被在事件队列中排在其前面的主线程上的任何重型处理所阻塞。

我发布了一个参考实现(由我编写)在GitHub上这里有演示

优点

  • 与屏幕大小更改检测方法不同,当控制台在单独的窗口中时也可以使用。
  • 与用户操作拦截方法不同,无论什么用户操作都可以正常工作。
  • console.log 方法不同,在不妨碍控制台信息的情况下可以为多个打开关闭的控制台工作。
  • 与基本的计时器-调试器方法不同,由于调试语句位于Worker而不是主线程中,因此主线程不会被阻塞,eval-debugger语句防止禁用特定的调试器语句,因此检测不应由于繁忙的线程(主线程或其他Worker)而出现误判。

缺点

  • 用户可以禁用所有断点,这将禁用此检测方法。
  • 使用eval包装的调试器语句在那些通过内容安全策略禁用eval的站点上无法工作,此时只能使用常规的调试器语句。

你在写这个的过程中是否使用了ChatGPT? - Hemant
1
@Hemant 这是在ChatGPT发布之前写的。不,我没有。 - starball

8

我发现新的方法在Chrome 89中可行

使用console.profile、setInterval和function toString

    var devtools = function() {};
    devtools.toString = function() {
        alert('NOPE!!')
        return '-'
    }

    setInterval(()=>{
        console.profile(devtools)
        console.profileEnd(devtools)
    }, 1000)

在Safari中,它不起作用。
在Chrome 89以下,我无法检查它是否起作用。

这个解决方案不适用于Chrome 70以下的版本。但是那些版本都太过时了,对吧?这个解决方案很棒。为什么没有人谈论它呢? - Uahnbu Tran
这是Chrome > 89目前最佳的解决方案。问题:它可能会被任何误报触发吗? - user1548936
1
这在我的Chrome 99.0.4844.51上不再起作用。 - Peter Harrison
1
它仍然可以在99.0.4844.51版本上运行,但是会产生大量的误报,如果您不介意,它仍然会被触发。 - user1548936

4
Chrome开发者工具实际上只是WebKit WebCore库的一部分。因此,这个问题适用于Safari、Chrome和任何其他使用WebCore的应用程序。
如果存在解决方案,它将基于当WebKit Web检查器打开和关闭时DOM中的差异。不幸的是,这是一种鸡生蛋蛋生鸡的问题,因为我们不能在检查器关闭时使用检查器来观察DOM。
您可能能够编写一些JavaScript来转储整个DOM树。然后在检查器打开时运行一次,在检查器关闭时再运行一次。DOM中的任何差异可能是Web检查器的副作用,我们可以使用它来测试用户是否正在检查或不检查。
这个链接是一个很好的DOM转储脚本的起点,但您需要转储整个DOMWindow对象,而不仅仅是document
更新:
看起来现在有一种方法可以做到这一点。请查看Chrome Inspector Detector

2
开发者提到,Chrome Inspector Detector在Google Chrome上已经不再起作用。 - Angelo
不仅是@Angelo所写的,而且你部分地正确地观察到了不同的浏览器,但是说“当检查器关闭时,我们无法使用检查器观察DOM”=>好吧,你可以,你可以发送一个XMLHttpRequest到一个Web服务,它会加载一个布尔值来检测检查器打开/关闭,并且关闭会发送响应,独立于您的客户端控制台是否无法获得。 - clusterBuddy

4
我写了一篇关于这个问题的博客文章:http://nepjua.org/check-if-browser-console-is-open/。它可以检测是否停靠或未停靠。
function isConsoleOpen() {  
  var startTime = new Date();
  debugger;
  var endTime = new Date();

  return endTime - startTime > 100;
}

$(function() {
  $(window).resize(function() {
    if(isConsoleOpen()) {
        alert("You're one sneaky dude, aren't you ?")
    }
  });
});

3
好的,但这会使页面变得陈旧,除非用户点击“继续”按钮,否则不会显示任何消息。这对用户来说会非常烦扰。 - guya
4
下一个“种族危险”的解决方案。非常错误。顺便说一下,“debugger”命令可以被禁用。 - 18C
1
我创建了一个类似的解决方案 https://dev59.com/nmsz5IYBdhLWcg3wlY0n#68494829,它不会阻塞主线程。限制在存储库自述文件中列出。此外,我注意到Chrome 91(可能还有其他版本)在关闭开发工具后会“忘记”“禁用所有断点”的设置,这可能是好事或坏事,取决于您想要什么,但我认为对大多数人来说这是好事。 - starball

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接