我有一个Chrome应用,我想在分辨率更改时正确调整大小(尺寸与屏幕宽度成比例)。我编写了一个函数,可以用正确的尺寸重绘应用程序,现在我只想在需要时执行它。
分辨率的改变会导致screen.width发生变化,但是(因为它们与不同的事物有关,所以可能不足为奇)“调整大小”事件没有触发,据我所知没有触发任何事件。
我了解Proxy对象(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy),所以我写了一些代码来检测何时设置了变量并执行我的回调,这似乎起作用,但在分辨率更改的情况下不起作用。
因此,我在线搜索并尝试了https://gist.github.com/eligrey/384583(本质上,这是在一个类似主题的几个stackoverflow问题中提供的答案,实际上是我最初产生的结果,尽管缺少新的Proxy对象提供的抽象概念)。
这似乎也可行(从某种意义上说,如果我说在完成screen.watch(“ width”,()=> console.log(“ hello”))之后手动设置screen.width);然后再进行屏幕显示。 width = 100;我的回调已执行)。但不是在分辨率改变的情况下(实际上,也许最重要的是,分配此监视程序似乎阻止了screen.width的分配)。
我有三个问题
1)当我分配使事情混乱的观察者/代理时,会发生什么情况。
2)我如何才能在这种详细程度下了解浏览器的功能(是什么发送触发来更改screen.width的?我猜是操作系统,这是什么样的?)
3)是否有更好的方法来实现我最初想要的功能(调整chrome应用程序的大小)。
大多数情况下,我对问题1)感兴趣,并且不再关心3)。
要复制我的问题,
编辑-正如Bertrand回答后所揭示的,resize事件实际上可能会在分辨率更改时触发,但这似乎是对分辨率更改的响应,如果窗口很小,则迫使窗口的边界变小。 screen.width可以更改,而无需触发调整大小事件。
答案 0 :(得分:3)
当我分配一个混乱的观察者/代理时发生了什么。
问题是width
属性实际上是Screen.prototype
上的 getter 。它不是window.screen
上的普通值:
console.log(window.screen.width);
console.log(window.screen.hasOwnProperty('width'));
// The property exists on the prototype instead, and is a getter:
const descriptor = Object.getOwnPropertyDescriptor(Screen.prototype, 'width');
console.log(descriptor);
如果width
属性是普通属性,仅由一个普通值组成,该值是浏览器通过eval
ing设置的
window.screen.width = 1500
然后可以使用代理或Object.watch
polyfill,因为您可以拦截对.width
属性的分配。这就是为什么在将自己的设置器分配给window.screen.width
之后看到的原因:
执行
screen.width = 100;
并观察到您好已记录,并且screen.width设置为100
它向您显示hello
-您正在调用先前分配给screen
的{{1}}属性的设置器。相反,由于本机内置属性不是浏览器分配给它的普通值,因此当屏幕变化时,window
不会被记录。这种情况是 bit ,类似于本示例代码片段中的操作:
hello
如您所见,添加到外部作用域const obj = (() => {
let privateVal = 'propOriginal';
// privateVal changes after 500ms:
setTimeout(() => {
console.log('Changing privateVal to propChanged');
privateVal = 'propChanged';
}, 500);
// Return an object which has a getter, which returns privateProp:
return {
get privateProp() {
return privateVal;
}
};
})();
// At this point, if one only has a reference to `obj`,
// there is no real way to watch for changes to privateVal:
console.log(obj.privateProp);
// Assigning a custom setter to obj.privateProp
// will only result in observing attempted assignments to obj.privateProp
// but will not be able to observe the change to privateVal:
Object.defineProperty(obj, 'privateProp', { set(newVal) {
console.log('Observed change: new value is ' + newVal);
}});
setTimeout(() => {
obj.privateProp = 1000;
}, 2500);
中的setter函数无法捕获对obj
的更改。 privateVal
并非完全如此,但这是相似的。您无权访问底层代码结构,该代码结构会导致通过调用内置getter返回的值发生更改。
window.screen
上内置的getter函数由本机代码组成-这意味着它不是普通的Javascript函数。由本机代码组成的功能始终是浏览器提供的功能(或运行代码的任何环境);您自己编写的任何Javascript函数通常都无法模仿它们。例如,window.history
对象仅 可以执行诸如screen
之类的历史记录操作;如果history.back()
被覆盖,并且您没有保存对它或任何其他window.history
对象的引用,则无法编写自己的函数来执行history
,因为{{ 1}}调用特权本机代码,该代码需要在可见Javascript和浏览器引擎之间建立接口。
类似地,内置history.back()
的getter在被调用时会返回纯Javascript无法直接观察的值。如果不进行轮询,监视更改的唯一其他方法是监听.back()
事件,如其他答案所述。 (此window.screen
事件类似于resize
返回的基础值,由浏览器内部函数管理,否则无法观察。)
如果resize
事件没有因分辨率的改变而被触发-例如,如果浏览器窗口已经足够小,则较小的分辨率不需要进行更改-则分辨率改变了不会导致任何事件被触发,这意味着除了轮询之外,没有其他方法可以监听它。 (You can try it for yourself,看起来好像没有其他事件是在分辨率更改时触发的)
一个人可能会编写(或调整)一个浏览器引擎,该监听器会侦听来自操作系统的分辨率变化并在发现时调度Javascript事件,但是这样做需要的知识远远超出Javascript-随时浏览Chromium's source code详细信息,但它相当复杂,对于没有使用过的语言经验的人来说可能是不透明的。
答案 1 :(得分:2)
According to Mozilla,仅在window
对象上触发屏幕调整大小事件。因此,您应该将监听器添加到window
而不是screen
中:
window.addEventListener('resize', yourfunc)
在Windows 10上进行了测试,它在Chrome上的魅力十足。值得一提的是,浏览器计算出的实际屏幕尺寸使用的是屏幕分辨率和。
例如,如果您使用1920x1080,请缩放100%:
resize
似乎未触发resize
尽管如此,还是有一个陷阱。仅当屏幕分辨率更新实际触发浏览器窗口大小调整时,才会检测到屏幕分辨率更新。考虑到浏览器窗口的窗口化/全屏初始状态以及屏幕尺寸的变小/变大,这并不是很明显。