$(this)和this、index() 、.hover()

本文详细解析了jQuery中的$(this)与html元素对象this的区别与应用,包括DOM操作、事件处理等核心概念。

1.$(this)和this

 

   $(this)是jQuery对象,可以使用jQuery的方法,如:click(),keyup()...

$(document).ready(function() {
   $("#orderedlist li:last").hover(function() {
     $(this).addClass("green");
   },function(){
 $(this).removeClass("green"); # $(this)
   });
 });


 

   this是html元素对象,能调用元素属性, 如:this.id  this.value...

$(document).ready(function() {
   // use this to reset several forms at once
   $("#reset").click(function() {
     $("form").each(function() {
       this.reset(); # this
     });
   });
 });


 

 

2.jQuery DOM 元素方法 - index()方法:返回指定元素的引索,.hover(function(){},function(){}),鼠标移入、移除

<script type="text/javascript">
	$("li:eq(0),li:eq(1),li:eq(5)").addClass("container_more");
	$("li:eq(2),li:eq(3),li:eq(4)").addClass("container_less");
      mm();
function mm(){
         $("li:eq(0)").hover(
              function(){
                 getChange($(this).index(this));
              }
          ,function(){alert("11")}
          );
     }

      function getChange(temp){
          switch(temp){
              case 0 : $("li:eq(0)").css("background","pink").html("<p class='word'>每日一练<p>"  ); break;
              case 1 : $("li:eq(1)").css("background","pink").html("<p class='word'>每日一练<p>"  ); break;
              case 2 : $("li:eq(2)").css("background","pink").html("<p class='word'>每日一练<p>"  ); break;
              case 3 : $("li:eq(3)").css("background","pink").html("<p class='word'>每日一练<p>"  ); break;
              case 4 : $("li:eq(4)").css("background","pink").html("<p class='word'>每日一练<p>"  ); break;
              case 5 : $("li:eq(5)").css("background","pink").html("<p class='word'>每日一练<p>"  ); break;
        }
      }
</script>


 

 

 

 

$(function () { // 菜单交互 var delayTime = []; $(".menu li").each(function (index) { $(this).hover( function () { var _self = this; delayTime[index] = setTimeout(function () { $(_self).find("ul").slideDown(300); }, 200); }, function () { clearTimeout(delayTime[index]); $(this).find("ul").slideUp(300); } ); }); // 首页 -> 传媒交互 $(".media-list li").click(function () { $(this) .find(".item") .slideDown(400) .parent() .siblings() .find(".item") .slideUp(400); }); // 子页 -> 科技菜单交互 /***********Begin***********/ $("#tech-list li a").hover( function () { // 鼠标进入:添加 cur 类 $(this).addClass("cur"); }, function () { // 鼠标离开:移除 cur 类 $(this).removeClass("cur"); } ); /***********End***********/ // 子页 -> 传媒菜单交互 var delayMedia = []; $("#media-nav>li").each(function (index) { $(this).hover( function () { var _self = this; delayMedia[index] = setTimeout(function () { $(_self) .find("ul") .slideDown(700) .end() .siblings() .find("ul") .slideUp(700); $(_self) .find(".frista") .addClass("cur") .parent() .siblings() .find(".frista") .removeClass("cur"); }, 300); }, function () { clearTimeout(delayMedia[index]); } ); }); }); $(document).bind("contextmenu", function (e) { return false; }); 融合工厂的项目中存在着不同的页面交互,而我们现在则需要对科技菜单进行鼠标悬停后有不同的样式显示,而我们的菜单 html 节点是这个样子的: <ul id="tech-list"> <li> <a>A</a> </li> <li> <a>A</a> </li> </ul> 现在,我们需要通过 jQuery 来针对 id 为 tech-list 下的 a 标签进行鼠标悬停后,补上 class,样式名为 cur。 平台测试未通过
最新发布
11-22
<template> <div class="content"> <div class="breadcrumd header"> <BreadcrumbHeader> <template #breadcrumb> <el-breadcrumb-item v-if="!isEdit"> {{ $t('新增') }} </el-breadcrumb-item> <el-breadcrumb-item v-else> {{ $t('编辑') }} </el-breadcrumb-item> </template> <template #expand> <div class="btn"> <el-button v-preventReClick @click="back"> {{ $t('Back') }} </el-button> <el-button v-preventReClick type="primary" @click="sure"> {{ $t('确定') }} </el-button> </div> </template> </BreadcrumbHeader> </div> <div class="context"> <MyCard :title=" productCategory === 'opticalModule' ? $t('光模块组基础信息') : $t('产品模型基础信息') " > <FormModel ref="formModel" :type="type" :isEdit="isEdit" :isClassify="true" :needTitle="false" :engineerProduct="true" ></FormModel> </MyCard> <MyCard :title="$t('配置列表')" class="list"> <el-row> <el-col :span="8"> <el-input v-if="isEdit && productCategory !== 'opticalModule'" v-model.trim="condition" clearable class="config_input" :placeholder="$t('搜索')" @keyup.enter="enterSearch" @clear="enterSearch" > <template #suffix> <el-icon><Search /></el-icon> </template> </el-input> </el-col> <el-col :offset="12" :span="4" class="btn_col"> <el-button type="primary" style="margin-bottom: 12px" @click="addconfigData"> {{ $t('新增配置') }} </el-button> </el-col> </el-row> <DynamicTable ref="configTable" type="Virtual_Engineer_Product_Config" :needIndex="true" :tableData="configData" :needExpand="true" :needShowImg="['istatus']" :imgAddress="imgAddress" :formatDiction="['istatus', 'source']" :dictionTypes="['Virtual_Engineer_Product_Config']" :dynamicField="['config_name']" :dynamicSlotName="['action']" :toggleRow="expandChange" > <template #action="props"> <span v-if="props.row.source_data === 'manual' || props.row.source === 'custom'" class="action_btn" @click="deleteConfigData(props.row)" > {{ $t('删除') }} </span> </template> <template #expand="props"> <DynamicTable ref="productTable" :type=" productCategory === 'opticalModule' ? 'Virtual_Engineer_Optical_Module_Product_List' : 'Virtual_Engineer_Product_List' " :needIndex="true" :tableData="supplyData" :parentData="props.row" :needShowImg="['istatus']" :imgAddress="imgAddress" :formatDiction="['istatus', 'release_status']" :dictionTypes="[ productCategory === 'opticalModule' ? 'Virtual_Engineer_Optical_Module_Product_List' : 'Virtual_Engineer_Product_List', ]" :dynamicField="['supply_product', 'count']" :dynamicSlotName="['action']" @productChange="productChange" > <template #action="scoped"> <span v-if="parantData.source_data === 'manual' || parantData.source === 'custom'" class="action_btn" @click="deleteProductData(scoped.row)" > {{ $t('删除') }} </span> </template> </DynamicTable> <el-row v-if="showProductAdd" type="flex" justify="center" class="add_btn"> <el-col :span="24"> <div @click="addProductData"> <el-icon><Plus /></el-icon> <span>{{ $t('新增产品') }}</span> </div> </el-col> </el-row> </template> </DynamicTable> </MyCard> </div> </div> </template> <script> import BreadcrumbHeader from '@/components/BreadcrumbHeader'; import MyCard from '../components/MyCard.vue'; import DynamicTable from './engineerComponent/dynamicTable.vue'; import FormModel from './engineerComponent/normalForm.vue'; import { getEngineerProductConfigAPI, saveProductMappingConfigAPI, saveOpticalModuleProductInfoAPI, } from '@/apis/ptp.js'; import { Plus, Search } from '@element-plus/icons-vue'; import { each } from 'lodash'; import pinia from '@/store/pinia'; import { useProductMgmtStore } from '@/store/modules/productMgmt'; export default { components: { BreadcrumbHeader, MyCard, FormModel, DynamicTable, Plus, Search, }, setup() { const productStore = useProductMgmtStore(pinia); return { productStore, }; }, data() { return { type: '', isEdit: true, configData: [], condition: '', supplyData: [], engineerProduct: '', productCategory: '', parantData: {}, showProductAdd: true, imgAddress: { 'istatus': { Y: new URL('../images/Status/completed.png', import.meta.url).href, N: new URL('../images/Status/offline.png', import.meta.url).href, }, }, }; }, created() { this.isEdit = this.$route.query.id ? true : false; this.type = this.$route.query.type; this.productCategory = this.$route.query.productCategory; if (this.isEdit) { this.engineerProduct = this.$route.query.engineerProduct; this.getConfigTableData(); } this.productStore.setResetPage(false); console.log(' this.productStore.setResetPage', this.productStore.setResetPage); }, methods: { enterSearch() { this.getConfigTableData(); }, addconfigData() { let index = this.$refs.configTable.dynamicIndex; this.configData.push({ dynamicIndex: index + 1, source: 'custom', 'source_data': 'manual', version: 1, istatus: 'Y', }); }, getConfigTableData() { let { engineerProduct, condition } = this; let params = { engineerProduct, condition, }; getEngineerProductConfigAPI({ params }) .then(res => { if (res.success) { this.configData = res.data; } }) .catch(() => { // catch空值 }); }, deleteConfigData(row) { this.$confirm(this.$t('确认删除'), this.$t('提示'), { confirmButtonText: this.$t('确定'), cancelButtonText: this.$t('取消'), type: 'warning', }) .then(() => { let tableData = this.configData.filter(v => v.dynamicIndex !== row.dynamicIndex); this.configData = tableData; }) .catch(() => { // catch空值 }); }, deleteProductData(row) { for (let i = 0; i < this.supplyData.length; i++) { if (this.supplyData[i].dynamicIndex === row.dynamicIndex) { this.supplyData.splice(i, 1); return; } } }, productChange(key, row) { each(this.supplyData, v => { if (key.value === v.supply_product && v.dynamicIndex !== row.dynamicIndex) { this.$message.error(this.applyLang('供应产品名称重复', 'Duplicate supply product name.')); row['supply_product'] = ''; return; } if (v.dynamicIndex === row.dynamicIndex) { v.istatus = key.status; v.comments = key.comments; v['release_status'] = key.releaseStatus; } }); }, expandChange(row) { this.parantData = row; if ((row.source_data && row.source_data === 'manual') || row.source === 'custom') { this.showProductAdd = true; } else { this.showProductAdd = false; } if (row.mapping_product_list) { this.supplyData = row.mapping_product_list; } else { row['mapping_product_list'] = []; this.supplyData = row.mapping_product_list; } }, addProductData() { if (this.productCategory === 'Server' && this.supplyData.length >= 1) { this.$message.warning( this.applyLang( '机型组只能配置一个产品', 'Only one product can be configured for a model group.' ) ); return; } let index = this.$refs.productTable.dynamicIndex; this.supplyData.push({ dynamicIndex: index + 1, }); }, back() { let pathList = this.$route.path.split('/'); let path = pathList.slice(0, pathList.length - 1).join('/'); this.$router.push(path); }, isRepeat() { let result = true; const newArr = this.configData.map(item => item.config_name); each(newArr, (val, index) => { if (val && newArr.indexOf(val) !== index) { this.$message.error( this.applyLang( `序号${index + 1}配置名称重复`, `No. ${index + 1}: Duplicate configuration name.` ) ); result = false; } }); return result; }, verifyConfig() { let result = true; let flag = true; const tableData = this.configData.filter(v => v.source === 'custom'); if (!this.isRepeat()) { return (result = false); } // eslint-disable-next-line consistent-return each(tableData, val => { if (!val.config_name) { this.$message.error( this.applyLang( `序号${val.dynamicIndex + 1}配置名称为空`, `No. ${val.dynamicIndex + 1} Configuration name is empty.` ) ); return (result = false); } else { if (val.mapping_product_list) { if (!flag) { return (result = false); } each(val.mapping_product_list, v => { if (!v.supply_product || !v.count) { this.$message.error( this.applyLang( `序号${val.dynamicIndex + 1}产品配置有误`, `No. ${val.dynamicIndex + 1} Product Configuration Error` ) ); result = false; flag = false; } }); } else { this.$message.error( this.applyLang( `序号${val.dynamicIndex + 1}配置产品列表为空`, `No. ${val.dynamicIndex + 1} The product list is empty.` ) ); return (result = false); } return (result = false); } }); return result; }, assembleData() { let dataList = []; const tableData = this.configData.filter(v => v.source === 'custom'); each(tableData, val => { let mappingList = {}; if (val.id) { mappingList.id = val.id; } mappingList['config_name'] = val.config_name; if (val.mapping_product_list) { let productList = []; each(val.mapping_product_list, key => { productList.push({ count: key.count, 'supply_product': key.supply_product, }); }); mappingList['mapping_product_list'] = productList; } dataList.push(mappingList); }); return dataList; }, saveOpticalModuleProductInfoSure() { Promise.resolve(this.$refs.formModel?.reloadData()) .then(data => { if (data) { let result = this.verifyConfig(); // 校验配置列表 if (result) { this.saveOpticalModuleProductInfo(); } } }) .catch(() => { // catch空值 }); }, isSureSave(cb) { this.$confirm(this.$t('是否保存'), this.$t('提示'), { confirmButtonText: this.$t('确定'), cancelButtonText: this.$t('取消'), type: 'warning', }) .then(() => { if (cb) { cb(); } }) .catch(() => { // catch空值 }); }, saveOpticalModuleProductInfo() { this.isSureSave(() => { const baseInfo = this.$refs.formModel?.getFormData(); const params = { ...baseInfo, 'config_detail': this.assembleData(), }; if (!this.isEdit) { delete params.id; } saveOpticalModuleProductInfoAPI(params) .then(res => { if (res.success) { this.productStore.setResetPage(true); this.$message.success(this.$t('保存成功')); this.back(); } }) .catch(resData => { this.$message.error(resData.message); }); }); }, sure() { if (this.productCategory === 'opticalModule') { this.saveOpticalModuleProductInfoSure(); return; } let result = this.verifyConfig(); // 校验配置列表 if (result) { let data = this.assembleData(); this.isSureSave(() => { const params = { engineer_product: this.engineerProduct, mapping_config_list: data, }; saveProductMappingConfigAPI(params) .then(res => { if (res.success) { this.productStore.setResetPage(true); this.$message.success(this.$t('保存成功')); this.back(); } }) .catch(resData => { this.$message.error(resData.message); }); }); } }, }, }; </script> <style lang="less" scoped> .content { padding-bottom: 30px; } .header { margin-bottom: 20px; height: 40px; background-color: #fff; } .context { margin: 0 20px 20px; .list { margin-top: 12px; } } .action_btn { color: #526ecc; &:hover { cursor: pointer; font-weight: bold; } &:active { font-weight: normal; color: #adb0b8; } } .config_input { margin-bottom: 12px; input { height: 28px; } } .add_btn { margin-top: 7px; margin-bottom: 12px; .el-col { text-align: center; background-color: #eef0f5; height: 40px; line-height: 40px; color: #526ecc; span { margin-left: 10px; &:hover, &:active { font-weight: bold; } } &:hover { cursor: pointer; } } } .btn_col { text-align: right; display: flex; justify-content: flex-end; } </style> 点击确定按钮不触发条件
11-04
你提出的问题是: > **点击“确定”按钮不触发条件** 根据你的代码分析,这个问题的核心在于:**点击“确定”按钮后,预期应该进行表单校验、数据组装并提交保存,但实际没有任何反应或未按预期触发流程。** --- ### ✅ 问题定位与原因分析 我们从以下几个方面来排查: #### 1. `sure()` 方法是否被正确绑定? ✅ 已经绑定了: ```html <el-button type="primary" @click="sure">{{ $t('确定') }}</el-button> ``` ✔️ 没问题。 #### 2. `sure()` 方法逻辑是否完整? 查看 `sure()` 方法的实现: ```js sure() { if (this.productCategory === 'opticalModule') { this.saveOpticalModuleProductInfoSure(); return; } let result = this.verifyConfig(); // 校验配置列表 if (result) { let data = this.assembleData(); this.isSureSave(() => { const params = { engineer_product: this.engineerProduct, mapping_config_list: data, }; saveProductMappingConfigAPI(params) .then(res => { if (res.success) { this.productStore.setResetPage(true); this.$message.success(this.$t('保存成功')); this.back(); } }) .catch(resData => { this.$message.error(resData.message); }); }); } } ``` 关键点: - 如果 `productCategory !== 'opticalModule'`,会走 `verifyConfig()`。 - 只有当 `verifyConfig()` 返回 `true` 才能继续执行。 但是看 `verifyConfig()` 函数中存在严重逻辑错误 ❌: --- ### 🔥 关键 Bug:`verifyConfig()` 中的 `return false` 被 `each` 内部函数捕获,外部无法感知! ```js verifyConfig() { let result = true; let flag = true; const tableData = this.configData.filter(v => v.source === 'custom'); if (!this.isRepeat()) { return (result = false); // 这里没问题 } each(tableData, val => { if (!val.config_name) { this.$message.error(...); return (result = false); // ❌ 错误!这是在 each 回调里的 return,不会跳出 verifyConfig } else { if (val.mapping_product_list) { if (!flag) { return (result = false); // ❌ 同上 } each(val.mapping_product_list, v => { if (!v.supply_product || !v.count) { this.$message.error(...); result = false; flag = false; // 利用 flag 防止多次提示 } }); } else { this.$message.error(...); return (result = false); // ❌ 依然只是退出当前 each 回调 } return (result = false); // ❌ 多余且无意义 } }); return result; } ``` 📌 **致命问题**:你在使用 `lodash.each` 遍历数组时,在回调中 `return` 并不能终止循环或返回函数值,它只会退出本次迭代。 所以即使设置了 `result = false`,但由于没有提前终止遍历,后续可能又被覆盖,而且最终仍会返回初始的 `result` 值(可能是 `true`),导致校验失败却仍然放行或静默失败。 此外,`else` 分支中的 `return (result = false)` 是无效控制流。 --- ## ✅ 正确解决方案 ### ✅ 修改 `verifyConfig()` 方法为标准 `for...of` 循环以支持中断返回 ```js verifyConfig() { // 先检查名称重复 if (!this.isRepeat()) { return false; } const tableData = this.configData.filter(v => v.source === 'custom'); for (const val of tableData) { if (!val.config_name) { this.$message.error( this.applyLang( `序号${val.dynamicIndex + 1}配置名称为空`, `No. ${val.dynamicIndex + 1} Configuration name is empty.` ) ); return false; } const productList = val.mapping_product_list; if (!productList || productList.length === 0) { this.$message.error( this.applyLang( `序号${val.dynamicIndex + 1}配置产品列表为空`, `No. ${val.dynamicIndex + 1} The product list is empty.` ) ); return false; } for (const product of productList) { if (!product.supply_product || !product.count) { this.$message.error( this.applyLang( `序号${val.dynamicIndex + 1}产品配置有误`, `No. ${val.dynamicIndex + 1} Product Configuration Error` ) ); return false; } } } return true; } ``` --- ### ✅ 补充说明其他潜在风险点 | 问题 | 说明 | |------|------| | `applyLang` 方法缺失 | 你在多处调用了 `this.applyLang(...)`,但在 `methods` 或 `computed` 中并未定义此方法,会导致 JS 报错 → **阻止 click 触发后续逻辑** | | 解决方案 | 添加国际化处理方法,或直接用 `$t` 替代 | 🔧 建议替换所有 `this.applyLang(a, b)` 为: ```js this.$t('序号{index}配置名称为空', { index: val.dynamicIndex + 1 }) ``` 并在语言包中配置动态文本: ```json { "序号{index}配置名称为空": "No. {index} Configuration name is empty." } ``` 否则 `applyLang is not a function` 会抛出异常,导致整个 `sure()` 被中断。 --- ### ✅ 推荐补充:添加 try-catch 安全防护 为了防止未知错误导致点击无响应,可以包裹一下: ```js sure() { try { if (this.productCategory === 'opticalModule') { this.saveOpticalModuleProductInfoSure(); return; } const result = this.verifyConfig(); if (result) { const data = this.assembleData(); this.isSureSave(() => { const params = { engineer_product: this.engineerProduct, mapping_config_list: data, }; saveProductMappingConfigAPI(params) .then(res => { if (res.success) { this.productStore.setResetPage(true); this.$message.success(this.$t('保存成功')); this.back(); } else { this.$message.error(res.message || this.$t('保存失败')); } }) .catch(err => { console.error('保存接口异常:', err); this.$message.error(err.message || this.$t('请求异常')); }); }); } } catch (error) { console.error('确定按钮点击异常:', error); this.$message.error(this.$t('操作异常,请查看控制台')); } } ``` --- ### ✅ 最终修复总结 你需要做以下几件事: 1. ✅ 将 `verifyConfig()` 改成 `for...of` 循环,确保能正确返回 `false` 2. ✅ 删除或实现 `applyLang` 方法(推荐使用 `$t` 动态插值) 3. ✅ 确保没有 JS 错误阻塞执行(打开浏览器控制台查看是否有报错) 4. ✅ 给 `sure()` 加上 `try-catch` 提高健壮性 5. ✅ 确认 `this.isEdit`, `productCategory`, `engineerProduct` 等变量已正确定义 --- ### ✅ 示例:修复后的 `verifyConfig` ```js verifyConfig() { // 检查配置名重复 const names = this.configData .filter(v => v.source === 'custom') .map(item => item.config_name); for (let i = 0; i < names.length; i++) { if (names[i] && names.indexOf(names[i]) !== i) { this.$message.error( this.$t('序号{index}配置名称重复', { index: i + 1 }) ); return false; } } const customConfigs = this.configData.filter(v => v.source === 'custom'); for (const cfg of customConfigs) { if (!cfg.config_name) { this.$message.error( this.$t('序号{index}配置名称为空', { index: cfg.dynamicIndex + 1 }) ); return false; } const products = cfg.mapping_product_list; if (!products || products.length === 0) { this.$message.error( this.$t('序号{index}配置产品列表为空', { index: cfg.dynamicIndex + 1 }) ); return false; } for (const p of products) { if (!p.supply_product) { this.$message.error( this.$t('序号{index}供应产品不能为空', { index: cfg.dynamicIndex + 1 }) ); return false; } if (!p.count) { this.$message.error( this.$t('序号{index}数量不能为空', { index: cfg.dynamicIndex + 1 }) ); return false; } } } return true; } ``` 同时,在语言文件中加入这些模板: ```json "序号{index}配置名称为空": "No. {index} Configuration name is empty.", "序号{index}配置名称重复": "No. {index} Duplicate configuration name.", "序号{index}配置产品列表为空": "No. {index} The product list is empty.", "序号{index}供应产品不能为空": "No. {index} Supply product cannot be empty.", "序号{index}数量不能为空": "No. {index} Count cannot be empty." ``` --- ## ✅ 总结 你现在遇到“点击确定没反应”的根本原因是: > ❌ `verifyConfig()` 使用了 `each` 导致 `return false` 不生效 > ❌ 存在 `applyLang is not defined` 的 JS 异常中断执行 > ✅ 修复方式:改用 `for...of` + 实现国际化方法 + 添加异常捕获 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值