element.onclick = fun与element onclick="fun()"的区别

原本标题是:element.onclick = fun与<element οnclick="fun()">的区别,但是提示含有非法字符。

问题背景:

在看this指向的时候看到了这个,有的时候通过给DOM元素绑定监听事件,来触发某些事件处理函数(如点击当前元素时修改该元素样式等),如果绑定的监听函数里面用到了this,上面两种方式可能会有不同的效果。

试验代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>区别onclick不同绑定方式</title>
</head>
<body>
<div id="div1" style="width: 80px;height: 80px" >DIV1,点击触发更改颜色</div>
<div id="div2" style="width: 80px;height: 80px" onclick="changeBackground2()">DIV2,点击触发更改颜色</div>
<script>
  //this指向当前元素,本质是调用对象里面的方法
  div1.onclick = changeBackground1;
  function changeBackground1(){
    console.log(this);
    this.style.color = '#cc0000';
  }

  //this指向window对象,本质是调用了嵌套函数,先调用onclick(),再调用changeBackground()
  //嵌套函数中的this要么指向window(非严格模式)要么undefined(严格模式)
  function changeBackground2() {
    console.log(this);
    this.style.color = '#cc0000';  //TypeError:color undefined
  }
</script>
</body>
</html>

试验结果:

可以很明显的看到,第一种方式达到了想要的效果,第二种方式直接报错了。通过控制台打印的内容和报的错能够分析出基本原因,this指向不同,第一个指向了div元素(div1),颜色修改成功,第二个指向了window对象,找不到color属性报错。为什么this的指向会不一样?

原因分析:

1.copying

element.onclick = doSomething;

这个实际上是把doSomthing这个函数复制一份给了element的onclick属性,因此触发点击事件时,this指向的是运行时调用它的对象element。原解释:The function is copied in its entirety to the onclick property (which now becomes a method). So if the event handler is executed this refers to the HTML element and its color is changed.

------------ window --------------------------------------
|                                                        |
|                                                        |
|                                                        |
|   ----------------                                     |
|   | HTML element | <-- this         -----------------  |
|   ----------------      |           | doSomething() |  |
|               |         |           -----------------  |
|          -----------------------          |            |
|          |copy of doSomething()|  <-- copy function    |
|          -----------------------                       |
|                                                        |
----------------------------------------------------------

2.referring

<element onclick="doSomething()">

这种方式只是让onclick属性指向了函数doSomething,并没有把doSomething函数的内部实现也包含进来,因此当触发点击事件时,只是找到onclick属性,然后告诉你去找doSomething吧,我这没具体实现,找到doSomething后,这时候里面的this已经指向window了,其实是在window下调用的doSomething函数。原解释:

you do not copy the function! Instead, you refer to it, and the difference is crucial. The onclick property does not contain the actual function, but merely a function call:

doSomething();

So it says “Go to doSomething() and execute it.” When we arrive at doSomething()the this keyword once again refers to the global window object and the function returns error messages.

------------ window --------------------------------------
|                                          / \           |
|                                           |            |
|                                          this          |
|   ----------------                        |            |
|   | HTML element | <-- this         -----------------  |
|   ----------------      |           | doSomething() |  |
|               |         |           -----------------  |
|          -----------------------         / \           |
|          | go to doSomething() |          |            |
|          | and execute it      | ---- reference to     |
|          -----------------------       function        |
|                                                        |
----------------------------------------------------------

3.两个的区别就是:第一种方式是完全复制;第二种只是指向了函数。

3.1

element.onclick = doSomething;
alert(element.onclick)
//结果如下

function doSomething()
{
	this.style.color = '#cc0000';
}

doSomething是element对象的一个属性(方法),类似下面:

var element = {
            value: "123",
            onclick: function doSomething() {
                console.log(this.value);
            }
        }
element.onclick();//123

3.2 

<element onclick="doSomething()">
alert(element.onclick)
//结果如下

function onclick()
{
	doSomething()
}

doSomething是一个嵌套函数,类似下面:

var element = {
       value: "123",
       onclick: function onclick(){
           doSomething();
       }
    }
function doSomething() {
    console.log(this.value);
}
element.onclick();//undefined

嵌套函数中的this要么指向window(非严格模式),要么undefined(严格模式)

参考链接:https://www.quirksmode.org/js/this.html

https://blog.youkuaiyun.com/u013584334/article/details/81192899

package com.example.kanban import android.annotation.SuppressLint import android.graphics.Bitmap import android.os.Build import android.os.Bundle import android.os.Handler import android.os.Looper import android.util.Log import android.view.WindowManager import android.webkit.* import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import kotlin.math.max class MainActivity : AppCompatActivity() { private lateinit var webView: WebView private val handler = Handler(Looper.getMainLooper()) private var timeoutRunnable: Runnable? = null private var isMonitoringRequests = false private var pendingRequests = 0 private var lastRequestTime: Long = 0 @SuppressLint("SetJavaScriptEnabled") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 启用硬件加速 window.setFlags( WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED ) webView = findViewById<WebView>(R.id.webView).apply { // 基础设置 settings.apply { databaseEnabled = true setGeolocationEnabled(true) javaScriptCanOpenWindowsAutomatically = true loadsImagesAutomatically = true javaScriptEnabled = true domStorageEnabled = true allowContentAccess = true allowFileAccess = true setSupportZoom(true) builtInZoomControls = true displayZoomControls = false cacheMode = WebSettings.LOAD_DEFAULT useWideViewPort = true loadWithOverviewMode = true // 提高渲染优先级 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mixedContentMode = WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE } // 解决ORB和安全问题 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { allowUniversalAccessFromFileURLs = true } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { safeBrowsingEnabled = false } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mixedContentMode = WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE } } // 添加JavaScript接口 addJavascriptInterface(WebAppInterface(), "Android") webViewClient = object : WebViewClient() { override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { super.onPageStarted(view, url, favicon) pendingRequests = 0 isMonitoringRequests = true startRequestMonitoring() } override fun onPageFinished(view: WebView, url: String) { super.onPageFinished(view, url) // 延迟注入脚本,确保页面完全初始化 handler.postDelayed({ injectRequestMonitoringScript() // 添加检查机制,防止某些请求未被监控到 webView.evaluateJavascript(""" setTimeout(function() { if (window.requestTracker && window.requestTracker.activeRequests.size > 0) { Android.requestStarted('unmonitored_requests'); setTimeout(function() { Android.requestCompleted('unmonitored_requests', true); }, 5000); } }, 3000); """, null) }, 500) } override fun onReceivedError( view: WebView, request: WebResourceRequest, error: WebResourceError ) { super.onReceivedError(view, request, error) handleWebError("加载错误: ${error.description}") } override fun onReceivedHttpError( view: WebView, request: WebResourceRequest, errorResponse: WebResourceResponse ) { super.onReceivedHttpError(view, request, errorResponse) handleWebError("HTTP错误: ${errorResponse.statusCode}") } // 捕获URL变化 override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean { if (request != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Log.d("WebView", "Loading URL: ${request.url}") // 这里可以添加处理URL变化的逻辑 } return super.shouldOverrideUrlLoading(view, request) } } } loadUrlWithRetry( "http://192.168.16.69:10011/#/show-draw?v=T1AnDCLWYo1fi0Ngzg5rW47U9W5joYwQ2F8aK1ENVaErtPWTiOlCN5y63ZsMTQObmlqGVKjmUceKYcvRfg%2BSXA%3D%3D&satoken=9a4d0de4-927d-41a0-b5c5-4440a0ddaae0", maxRetries = 2 ) } private fun startRequestMonitoring() { isMonitoringRequests = true pendingRequests = 0 timeoutRunnable?.let { handler.removeCallbacks(it) } timeoutRunnable = Runnable { if (isMonitoringRequests && pendingRequests > 0) { handleWebError("请求超时,仍有 $pendingRequests 个请求未完成") } }.also { handler.postDelayed(it, 30000) // 30秒超时 } } private fun injectRequestMonitoringScript() { webView.evaluateJavascript(""" (function() { if (!window.requestTracker) { window.requestTracker = { activeRequests: new Set(), completedRequests: new Set(), trackRequest: function(id) { this.activeRequests.add(id); Android.requestStarted(id); return function(success, id) { this.activeRequests.delete(id); this.completedRequests.add(id); Android.requestCompleted(id, success); // 检查是否所有请求都完成了 if (this.activeRequests.size === 0) { Android.allRequestsCompleted(); } }.bind(this); }, getActiveRequests: function() { return Array.from(this.activeRequests); } }; // 增强的XMLHttpRequest监控 var originalXHROpen = XMLHttpRequest.prototype.open; var originalXHRSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open = function(method, url, async) { this._requestId = 'xhr_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); this._tracker = window.requestTracker.trackRequest(this._requestId); return originalXHROpen.apply(this, arguments); }; XMLHttpRequest.prototype.send = function(data) { var self = this; this.addEventListener('readystatechange', function() { if (self.readyState === 4) { self._tracker(self.status >= 200 && self.status < 400, self._requestId); } }); return originalXHRSend.apply(this, arguments); }; // 增强的fetch监控 var originalFetch = window.fetch; window.fetch = function(input, init) { var requestId = 'fetch_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); var tracker = window.requestTracker.trackRequest(requestId); return originalFetch(input, init) .then(function(response) { tracker(response.ok, requestId); return response; }) .catch(function(error) { tracker(false, requestId); throw error; }); }; // 更全面的资源加载监控 function monitorResourceLoading() { // 监控静态资源 ['img', 'script', 'link'].forEach(function(tagName) { var elements = document.getElementsByTagName(tagName); Array.from(elements).forEach(function(el) { if ((tagName === 'link' && el.rel !== 'stylesheet') || (el.complete || el.readyState === 'complete')) { return; } var requestId = tagName + '_' + (el.src || el.href); var tracker = window.requestTracker.trackRequest(requestId); el.addEventListener('load', function() { tracker(true, requestId); }); el.addEventListener('error', function() { tracker(false, requestId); }); }); }); // 监控动态添加的元素 new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { mutation.addedNodes.forEach(function(node) { if (node.nodeType === 1) { // ELEMENT_NODE if (node.tagName === 'IMG') { var requestId = 'img_' + node.src; var tracker = window.requestTracker.trackRequest(requestId); node.addEventListener('load', function() { tracker(true, requestId); }); node.addEventListener('error', function() { tracker(false, requestId); }); } else if (node.tagName === 'SCRIPT' && node.src) { var requestId = 'script_' + node.src; var tracker = window.requestTracker.trackRequest(requestId); node.addEventListener('load', function() { tracker(true, requestId); }); node.addEventListener('error', function() { tracker(false, requestId); }); } else if (node.tagName === 'LINK' && node.rel === 'stylesheet') { var requestId = 'css_' + node.href; var tracker = window.requestTracker.trackRequest(requestId); node.addEventListener('load', function() { tracker(true, requestId); }); node.addEventListener('error', function() { tracker(false, requestId); }); } } }); }); }).observe(document, { childList: true, subtree: true }); } // 延迟执行监控,确保所有初始请求都被捕获 setTimeout(monitorResourceLoading, 500); } })(); """.trimIndent(), null) } private fun loadUrlWithRetry(url: String, maxRetries: Int, currentRetry: Int = 0) { if (currentRetry > 0) { Toast.makeText(this, "正在重试 ($currentRetry/$maxRetries)...", Toast.LENGTH_SHORT).show() } webView.loadUrl(url) handler.postDelayed({ if (webView.progress < 90 && currentRetry < maxRetries) { loadUrlWithRetry(url, maxRetries, currentRetry + 1) } }, 10000) // 10秒后检查 } private fun handleWebError(message: String) { runOnUiThread { webView.loadData(""" <html> <body style='font-family:sans-serif;padding:20px;text-align:center;'> <h3 style='color:#d32f2f;'>加载遇到问题</h3> <p>$message</p> <button onclick="window.location.reload()" style='padding:10px 20px;background:#4285f4;color:white; border:none;border-radius:5px;margin-top:20px;'> 重新加载 </button> </body> </html> """, "text/html", "UTF-8") } } inner class WebAppInterface { @JavascriptInterface fun requestStarted(id: String) { pendingRequests++ lastRequestTime = System.currentTimeMillis() Log.d("WebView", "Request started ($id). Pending: $pendingRequests") // 延长超时时间 timeoutRunnable?.let { handler.removeCallbacks(it) } timeoutRunnable = Runnable { if (isMonitoringRequests && pendingRequests > 0) { handleWebError("请求超时,仍有 $pendingRequests 个请求未完成") } }.also { handler.postDelayed(it, 60000) // 延长到60秒 } } @JavascriptInterface fun requestCompleted(id: String, success: Boolean) { pendingRequests = max(0, pendingRequests - 1) Log.d("WebView", "Request completed ($id, success: $success). Pending: $pendingRequests") if (!success) { Log.e("WebView", "Request failed: $id") } } @JavascriptInterface fun allRequestsCompleted() { handler.post { isMonitoringRequests = false timeoutRunnable?.let { handler.removeCallbacks(it) } Log.d("WebView", "所有请求已完成") // 延迟执行,确保DOM更新完成 handler.postDelayed({ webView.evaluateJavascript(""" if (window.onAllRequestsComplete) { window.onAllRequestsComplete(); } """, null) }, 300) } } } private fun updatePageContent() { // 方法1: 执行JavaScript通知页面数据已更新 webView.evaluateJavascript(""" (function() { // 检查页面是否有更新数据的函数 if (window.onDataUpdated) { window.onDataUpdated(); } else { // 如果没有特定函数,尝试重新加载页面元素 document.dispatchEvent(new Event('dataUpdated')); } })(); """.trimIndent(), null) // 方法2: 可选的页面刷新(谨慎使用,可能导致无限循环) // webView.reload(); } override fun onDestroy() { handler.removeCallbacksAndMessages(null) webView.stopLoading() webView.destroy() super.onDestroy() } }ajax请求没有加载完成,页面就完成不加载了,只加载了一部分
05-14
<!-- 权限管理 --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <link rel="stylesheet" href="../../layui/css/layui.css"> <link rel="stylesheet" href="../../css/diy.css"> <style> .layui-body { overflow-x: scroll; } </style> </head> <body> <div class="section1"> <!-- 内容主体区域 --> <div class="manu auth" style="padding: 15px;"> <form class="layui-form" action=""> <div class="form-input-box-t layui-form-item"> <div class="input-box"> <label class="layui-form-label">权限名</label> <div class="layui-input-block input-i block"> <input type="text" name="mod_name" required lay-verify="required" autocomplete="off" class="layui-input"> <!--通过lay-verify="required"实现输入验证,确保必填字段非空--> </div> </div> <div class="input-box"> <!--查询部分--> <label class="layui-form-label">用户组</label> <div class="layui-input-block select block"> <select name="user_group" lay-filter="required" id="user_group"> <option value=""></option> </select> </div> </div> <div class="input-box"> <label class="layui-form-label">添加权限</label> <div class="layui-input-block select block"> <select name="add" lay-filter="required"> <option value=""></option> <option value="0">无</option> <option value="1">有</option> </select> </div> </div> <div class="input-box"> <label class="layui-form-label">修改权限</label> <div class="layui-input-block select block"> <select name="set" lay-filter="required"> <option value=""></option> <option value="0">无</option> <option value="1">有</option> </select> </div> </div> <div class="input-box"> <label class="layui-form-label">删除权限</label> <div class="layui-input-block select block"> <select name="del" lay-filter="required"> <option value=""></option> <option value="0">无</option> <option value="1">有</option> </select> </div> </div> <div class="input-box"> <label class="layui-form-label">查询权限</label> <div class="layui-input-block select block"> <select name="get" lay-filter="required"> <option value=""></option> <option value="0">无</option> <option value="1">有</option> </select> </div> </div> </div> </form> <div class="buts"> <button type="button" class="layui-btn layui-btn-normal" id="inquire"><span>查询</span></button> <button type="button" class="layui-btn layui-btn-normal" id="reset"><span>重置</span></button> </div> <div class="Customize-the-box"> <div class="circle"></div> <div class="circle"></div> <div class="card-inner"></div> </div> </div> <h1>数据列表</h1> <div class="table"> <table class="layui-hide" id="newsClassification" lay-filter="newsClassification"></table> <script type="text/html" id="toolbarDemo"> <div class="layui-btn-container"> <button class="layui-btn layui-btn-sm" lay-event="add">修改</button> </div> </script> </div> </div> <script src="../../layui/layui.js"></script> <script src="../../js/axios.min.js"></script> <script src="../../js/index.js"></script> <script src="../../js/base.js" charset="utf-8"></script> <script> var BaseUrl = baseUrl() layui.use(['element', 'layer', 'util'], function () { var element = layui.element , table = layui.table , layer = layui.layer , util = layui.util , $ = layui.$; let personInfo = JSON.parse(sessionStorage.personInfo) let user_group = personInfo.user_group let token = sessionStorage.token || null //头部事件 util.event('lay-header-event', { //左侧菜单事件 menuLeft: function (othis) { layer.msg('展开左侧菜单的操作', {icon: 0}); } , menuRight: function () { layer.open({ type: 1 , content: '<div style="padding: 15px;">处理右侧面板的操作</div>' , area: ['260px', '100%'] , offset: 'rt' //右上角 , anim: 5 , shadeClose: true }); } }); // table 事件 table.render({ elem: '#newsClassification' , toolbar: true , url: BaseUrl + '/api/auth/get_list' , headers: { 'x-auth-token': token, 'Content-Type': 'application/json' } ,limits: [10] , page: { //支持传入 laypage 组件的所有参数(某些参数除外,如:jump/elem) - 详见文档 layout: ['limit', 'count', 'prev', 'page', 'next', 'skip'] //自定义分页布局 //,curr: 5 //设定初始在第 5 页 , groups: 1 //只显示 1 个连续页码 , first: false //不显示首页 , last: false //不显示尾页 } , cols: [[ {type: 'checkbox'} , {field: 'user_group', width: '10%', title: '用户组'} , {field: 'mod_name', width: '10%', title: '权限名称', templet: function (d) { if (d.path.replace('/'+d.table_name+'/','')=='table') { return "<div>"+d.mod_name+"后台列表</div>" } if (d.path.replace('/'+d.table_name+'/','')=='view') { return "<div>"+d.mod_name+"后台详情</div>" } if (d.path.replace('/'+d.table_name+'/','')=='list') { return "<div>"+d.mod_name+"前台列表</div>" } if (d.path.replace('/'+d.table_name+'/','')=='details') { return "<div>"+d.mod_name+"前台详情</div>" } if (d.path.replace('/'+d.table_name+'/','')=='edit') { return "<div>"+d.mod_name+"前台编辑</div>" } } } , { field: 'add', width: '10%', title: '<span>添加</span>权限', templet: function (d) { if (d.add == '0') { return "<div>无</div>" } else { return "<div>有</div>" } } } , { field: 'del', width: '10%', title: '<span>删除</span>权限', templet: function (d) { if (d.del == '0') { return "<div>无</div>" } else { return "<div>有</div>" } } } , { field: 'set', width: '10%', title: '修改权限', templet: function (d) { if (d.set == '0') { return "<div>无</div>" } else { return "<div>有</div>" } } } , { field: 'get', width: '10%', title: '<span>查询</span>权限', templet: function (d) { if (d.get == '0') { return "<div>无</div>" } else { return "<div>有</div>" } } } , { field: 'create_time', width: '15%', title: '新增时间', sort: true, templet: "<div>{{layui.util.toDateString(d.create_time, 'yyyy-MM-dd HH:mm:ss')}}</div>" } , { field: 'update_time', width: '15%', title: '更新时间', sort: true, templet: "<div>{{layui.util.toDateString(d.update_time, 'yyyy-MM-dd HH:mm:ss')}}</div>" } , {field: 'operate', title: '操作', toolbar: "#toolbarDemo"} ]] , page: true, request: { limitName: 'size' }, response: { statusName: 'code', //规定返回/Back的状态码字段为code statusCode: 200 //规定成功的状态码味200 }, parseData: function (res) { return { "code": 200, "msg": "", "count": res.result.count, "data": res.result.list } }, where: {like: 0, size: 10} }); layui.table.on('tool(newsClassification)', function (obj) { //注:tool是工具条事件名,test是table原始容器的属性 lay-filter="对应的值" var data = obj.data; //获得当前行数据 var layEvent = obj.event; //获得 lay-event 对应的值(也可以是表头的 event 参数对应的值) var tr = obj.tr; //获得当前行 tr 的DOM对象 if (layEvent == "add") { let auth_id = data.auth_id // window.location = ('./view.html?' + auth_id) layopen_dateil('./view.html?' + auth_id) } }); // 请求参数: let request = {like: 0, size: 10, page: 1, mod_name: '', user_group: '',add:'',set:'',del:'',get:'',orderby: 'create_time desc'} // <span>重置</span>/Reset参数 let resetName = {like: 0, size: 10, page: 1, orderby: 'create_time desc'} fun('newsClassification', BaseUrl + '/api/auth/del', 'auth_id', request, resetName) async function get_list_user_group() { $.ajax({ url: BaseUrl + "/api/user_group/get_list", type: "get", async: false, success: function (data) { if (data && typeof data === 'string'){ data = JSON.parse(data); } if (data.result) { // 拿到单选框的父级节点 let select = document.querySelector("#user_group") let op1 = document.createElement('option') select.appendChild(op1) // 收集数据 长度 let count // 收集数据 数组 let arr = [] count = data.result.count arr = data.result.list for (let i = 0; i < arr.length; i++) { // 创建节点 let op = document.createElement('option') // 给节点赋值 op.innerHTML = arr[i].name op.value = arr[i].name // 新增/Add节点 select.appendChild(op) layui.form.render('select') } } } }); } get_list_user_group(); }); </script> </body> </html> 分析一下以上代码,不要自己加内容
最新发布
05-28
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值