问题在script标签的type="module" 上.... 不加这个type就正常了

==>
当在<script>标签中使用type="module"时,您告诉浏览器该脚本应当作JavaScript模块来处理。这意味着:
默认严格模式:模块自动处于严格模式下,您不需要在脚本顶部显式声明"use strict";。
模块作用域:在模块脚本中声明的变量、函数等不会自动成为全局作用域的一部分。它们只存在于模块作用域内,除非您显式地导出或导入它们。
支持模块导入和导出:您可以使用import和export语句在模块之间共享代码。

在使用Parcel这类构建工具时,它负责处理模块的打包和依赖管理。如果您的项目被构建为一个单一的打包文件,而您在引入这个文件时使用了type="module",那么可能会发生一些不一致的行为,因为Parcel已经将模块和其他代码转换成了浏览器可以直接执行的格式。具体表现取决于Parcel的版本和配置,以及您的项目结构。
如果不使用type="module"属性,脚本会被当作传统的JavaScript脚本来处理,这可能和Parcel构建的输出更加兼容,特别是如果Parcel已经处理了所有必要的模块转换。
如何决定是否使用type="module"?
如果您直接在HTML中编写或引入ES模块(也就是说,不通过构建工具处理),那么使用type="module"是正确和必要的。
如果您通过构建工具(如Parcel、Webpack等)构建项目,并且构建输出是一个自包含的脚本,那么通常不需要在引用构建后的脚本时使用type="module",除非您有特定的理由或构建工具的文档特别建议这样做。

使用 curl 查询 IP 信息

curl http://ip-api.com/json/

使用 geoiplookup 工具

安装 geoiplookup

查询 VPS 的位置

geoiplookup $(curl -s ifconfig.me)

使用 whois 命令
whois 命令可以查询 IP 地址的注册信息,这通常包含地理位置。
安装 whois

sudo yum install whois

查询VPS的位置

whois $(curl -s ifconfig.me)

检查本地 DNS 配置
确认本地的 DNS 配置没有影响到结果。可以尝试使用不同的 DNS 服务器来验证地理位置。您可以使用 Google 的公共 DNS 服务器 8.8.8.8 和 8.8.4.4,或者 Cloudflare 的公共 DNS 服务器 1.1.1.1 和 1.0.0.1。

配置本地 DNS
编辑 /etc/resolv.conf 文件:

sudo vim /etc/resolv.conf

将内容修改为:

nameserver 8.8.8.8
nameserver 8.8.4.4

SSH动态端口转发(也称为SSH隧道或SOCKS代理)是一个非常强大的功能
它能够通过SSH连接将本地计算机连接到另一个网络上的服务器,并将该服务器用作网络请求的代理。这个功能不仅可以用于安全地浏览互联网。

安全浏览

绕过网络限制: 如果你处于一个限制了访问特定网站的网络中,通过SSH动态端口转发可以安全地访问这些网站。
匿名浏览: 使用SSH隧道可以提高你浏览网站时的隐私性,因为你的网络流量会通过远程服务器转发。

开发与测试

访问受限的开发环境: 如果你需要访问部署在受限网络环境中的开发服务器,可以使用SSH隧道安全地访问这些资源。
数据库管理: 通过SSH隧道连接到远程数据库服务器,可以在本地运行数据库管理工具进行操作,而不需要直接暴露数据库端口。

远程工作

安全访问内部工具: 对于远程工作人员,通过SSH隧道可以安全地访问公司内网中的内部工具和服务。
文件传输: 虽然SCP和SFTP是更常用的SSH文件传输方法,但通过SSH隧道,你也可以安全地使用基于HTTP的文件管理界面。

游戏与娱乐

游戏:在某些情况下,使用SSH隧道可以减少游戏延迟或访问地区限制内容。

命令与配置

一个基本的SSH动态端口转发命令如下所示:

ssh -D 8080 -C -q -N 用户名@远程服务器地址
  • D 8080 指定了本地端口(在这个例子中是8080),SSH客户端会监听这个端口,并将通过SSH连接转发的流量作为SOCKS代理服务。
  • C 启用压缩,可以提高传输速度并减少数据使用。
  • q 启用静默模式,减少日志输出。
  • N 表示不执行远程命令,只进行端口转发。

使用 Parcel 插件处理静态文件

npm install parcel-plugin-static-files-copy --save-dev

在项目根目录创建一个 static 文件夹,并将 flowers.jpg 文件放入其中。

创建或更新 package.json 文件:

{
  "scripts": {
    "start": "parcel basic-fetch.html"
  },
  "staticFiles": {
    "staticPath": "static",
    "staticOutPath": "./"
  }
}

将 flowers.jpg 文件移动到 static 文件夹中:
...
运行项目

在JavaScript中,for...in和for...of是两种不同的循环语句,它们被用于遍历不同类型的数据结构。

for...in

描述:for...in循环用于遍历一个对象的属性。
行为:它迭代对象的所有可枚举属性,包括原型链上继承的属性。
适用场景:
当需要遍历对象属性名时使用。
适合用于对象自定义属性的迭代。
不推荐用于数组的迭代,因为它不仅遍历数组索引,还可能遍历自定义属性及原型链上的属性。

for...of

描述:for...of循环用于遍历可迭代对象的元素,如数组、字符串、Map、Set等。
行为:它迭代对象的值,而不是属性名。
适用场景:
当需要遍历数组、字符串、类数组对象(如NodeList)、Map、Set、以及实现了可迭代协议的自定义对象的值时使用。
适合用于遍历各种集合对象中的元素值。
不适用于普通对象,因为普通对象默认不是可迭代的。

// for...in 示例
const obj = { a: 1, b: 2, c: 3 };
for (const prop in obj) {
  console.log(`obj.${prop} = ${obj[prop]}`);
}

// for...of 示例
const arr = [1, 2, 3, 4, 5];
for (const value of arr) {
  console.log(value);
}

在for...in示例中,将会输出对象obj的每个属性和值。而在for...of示例中,将会输出数组arr中的每个数字值。
注意
使用for...in遍历数组时应当小心,因为该方法遍历的是对象的键,而不是值。如果数组对象上有附加属性或者数组原型被扩展,则这些属性也会被遍历。这不是遍历数组元素的推荐方法。而for...of则提供了一种简单而直接的方式来迭代数组元素。
总结来说,for...in主要用于遍历对象的属性,而for...of主要用于遍历具有迭代行为的集合对象的元素值。选择哪个循环语句取决于你的具体需求和正在操作的数据类型。

textContent、innerHTML和insertAdjacentHTML是JavaScript中用于操作DOM元素内容的三种不同方法。每种方法都有其特定的用途和行为。
textContent
描述:textContent属性用于获取或设置指定节点及其后代的文本内容。
特点:当设置内容时,它会替换元素的所有子节点,并且只能设置文本内容,不能解析HTML标签。
使用场景:适用于当你只需要操作文本,而不需要HTML标签时。这种方法防止了HTML注入,更加安全。
innerHTML
描述:innerHTML属性用于获取或设置HTML或XML元素中的HTML内容。
特点:可以解析HTML标签,并将其渲染成对应的元素。使用此属性设置HTML内容时,浏览器会解析字符串并创建新的DOM树。
使用场景:当你需要包含HTML标签的内容时使用,但需要注意避免跨站脚本(XSS)攻击。
insertAdjacentHTML
描述:insertAdjacentHTML方法将指定的文本解析为HTML或XML,并将生成的节点插入到DOM树中的指定位置。
特点:它不会重新解析调用它的元素(从而不会破坏元素内部已经存在的DOM结构),而只是添加新的元素,这使得它相对于innerHTML来说性能更优。
参数:接受两个参数,第一个是插入位置('beforebegin'、'afterbegin'、'beforeend'、'afterend'),第二个是要插入的HTML字符串。
使用场景:适用于需要将内容插入到元素内部或周围,而不是替换元素内部所有内容的情况。更适合动态添加内容,因为它允许更细致地控制插入的位置。

insertAdjacentHTML方法的使用场景包括但不限于以下几种:
动态内容插入:当你需要在页面上动态地插入新的HTML元素或内容时,insertAdjacentHTML可以让你指定插入的位置,而不需要替换或重写现有的元素内部HTML。
性能优化:如果你需要向页面中频繁添加元素,insertAdjacentHTML通常比innerHTML更高效,因为它不需要重新解析调用它的元素的内部HTML,这可以减少重绘和重排带来的性能开销。
避免直接DOM操作开销:直接使用DOM方法(如createElement和appendChild)创建和添加复杂的HTML结构可能很繁琐。相比之下,insertAdjacentHTML可以一次性插入整个HTML片段。
维持现有脚本和监听器:在不需要或不想干扰元素内部现有子节点的事件监听器或脚本运行的情况下,使用insertAdjacentHTML可以插入新内容而不影响现有内容。
页面模板填充:当使用模板文字来生成HTML内容时,可以使用insertAdjacentHTML将生成的内容插入到页面的指定位置。
快速原型和测试:在开发过程中,当需要快速测试或原型设计时,insertAdjacentHTML可以方便快速地将HTML片段加入页面。
插入位置参数
insertAdjacentHTML方法接收两个参数:第一个参数是一个表示相对位置的字符串,第二个参数是要插入的HTML字符串。第一个参数可以是以下四个值之一:
'beforebegin':在当前元素之前插入HTML。
'afterbegin':在当前元素的第一个子节点之前插入HTML。
'beforeend':在当前元素的最后一个子节点之后插入HTML。
'afterend':在当前元素之后插入HTML。
实例
假设你有一个列表,并且想要在列表的末尾添加新的列表项,可以使用insertAdjacentHTML如下:

var list = document.getElementById('myList');
list.insertAdjacentHTML('beforeend', '<li>新的列表项</li>');

这将在现有列表的最后添加一个新的

  • 元素,而不会干扰到列表中已有的其他列表项及其绑定的事件。

  • 针对Webkit浏览器(Chrome,Safari等)

    .instagram-slide .instagram-layout--scroll::-webkit-scrollbar {
        display: none; /* 隐藏滚动条 */
    }

    为了兼容其他浏览器,如Firefox,可以使用以下CSS:

    .instagram-slide .instagram-layout--scroll {
        -ms-overflow-style: none;  /* IE和Edge */
        scrollbar-width: none;  /* Firefox */
    }
    Posted in CSS.
    
    // one-liner version
    // retains latin-1 supplement chars as well as latin extended-a and latin extended-b
    
    Shopify.handleize = function (str) {
        return str.toLowerCase().replace(/[^\w\u00C0-\u024f]+/g, "-").replace(/^-+|-+$/g, "");
    };
    
    // from https://github.com/Shopify/liquid/blob/63eb1aac69a31d97e343822b973b3a51941c8ac2/performance/shopify/shop_filter.rb#L100
    Shopify.handleize = function (str) {
        str = str.toLowerCase();
    
        var toReplace = ['"', "'", "\\", "(", ")", "[", "]"];
    
        // For the old browsers
        for (var i = 0; i < toReplace.length; ++i) {
            str = str.replace(toReplace[i], "");
        }
    
        str = str.replace(/\W+/g, "-");
    
        if (str.charAt(str.length - 1) == "-") {
            str = str.replace(/-+\z/, "");
        }
    
        if (str.charAt(0) == "-") {
            str = str.replace(/\A-+/, "");
        }
    
        return str
    };
            getCursorLeft(e) {
                const event = e.changedTouches ? e.changedTouches[0] : e;
    //这行代码首先检查e.changedTouches是否存在,这是为了区分触摸事件(如移动设备)与鼠标事件(如桌面设备)。如果e.changedTouches存在,表示当前是一个触摸事件,因此event被赋予e.changedTouches[0],即首个触摸点。若不存在,直接使用e作为事件对象。
    
                const { left, width } = this.container.getBoundingClientRect();
    //使用getBoundingClientRect方法获取当前组件容器的left位置和width宽度。left代表容器左边缘相对于视口的水平位置,width是容器的宽度。
    
                const sliderButtonWidthPercent = (this.slider.getBoundingClientRect().width + 4) * 100 / width;
    //获取滑块按钮的宽度,并且稍微增加了一个固定值4(可能是为了考虑边缘的留白、边框或其他视觉效果),然后计算它相对于容器宽度的百分比占比。
    
                const min = sliderButtonWidthPercent / 2;
                const max = 100 - min;
    //基于滑块按钮的宽度比例,计算出滑块可以移动的最小和最大百分比值。这确保滑块不会超出容器的边界。
                const delta = event.pageX - left;
                let percent = (delta / width) * 100;
    //这里,event.pageX获取光标相对于文档(页面)左侧的位置。从这个值中减去容器的left值,可以得到光标在容器内的相对位置delta。再将这个相对位置转换为容器宽度的百分比(percent)。
                if (percent < min) percent = min;
                if (percent > max) percent = max;
    
                return percent
            }
        }

    在JavaScript中,函数内的this关键字的行为非常依赖于函数是如何被调用的。在user, user2, 和 user3的例子中,sayHi函数被定义了三种不同的方式,这导致this的行为也存在差异。

    user对象

    这里使用了一个名为sayHi的普通函数表达式:

    let user = {
        name: "Tom",
        sayHi: function () {
            console.log('Hi, I am ' + this.name);
        }
    }

    在这个例子中,sayHi是一个普通的函数属性。当它作为方法(即通过对象引用调用时,例如user.sayHi())被调用时,this指的是调用它的对象,即user。

    user2对象

    这里使用了一个ES6中的方法简写:

    let user2 = {
        name: "Jack",
        sayHi() {
            console.log('Hi, I am ' + this.name);
        }
    }

    user2例子中的sayHi定义和user的很相似,唯一的区别是它使用了ES6的简化语法来创建方法。这并不改变this的行为:作为user2对象的方法被调用时,this同样指向user2。

    user3对象

    这里使用了箭头函数:

    let user3 = {
        name: "Ben",
        sayHi: () => {
            console.log('Hi, I am ' + this.name);
        }
    }

    user3.sayHi使用了一个箭头函数,箭头函数不绑定this,相反,它们捕获定义时上下文的this值。在全局代码中定义的箭头函数中的this指向全局对象,在浏览器中通常是window。因此,无论你何时何地调用user3.sayHi,它打印出来的this.name总会引用定义时捕获的this的name属性,而不是user3的name属性。如果user3.sayHi在全局范围内定义,如在浏览器中使用<script>直接定义,则this.name很可能是undefined。

    关于setTimeout
    当你直接传递方法给setTimeout,如setTimeout(user.sayHi, 1000),你传递的是函数本身,而不是函数的调用。结果是,当setTimeout执行这个函数时,它没有了对象上下文,因此this不再引用原始的对象,除非像user和user2示例中一样使用.bind()方法明确地绑定this。
    总结下三者间的区别:
    user和user2展示了在对象内部定义方法的两种方式,都可以使用sayHi作为方法调用,其中this指向调用的对象。
    user3的箭头函数导致this在定义时就固定下来,对箭头函数使用bind是无效的,因为箭头函数不绑定this。
    使用.bind()可以修正setTimeout调用中this的指向,确保当方法在将来某个时间点执行时,this指向正确的对象。