节我们学习如何使用 jQuery 中的方法来添加和删除 HTML 元素。
jQuery 中用于添加 HTML 元素的方法有如下几种:
方法 | 描述 |
append() | 在所选元素的末尾插入内容 |
prepend() | 在选定元素的开头插入内容 |
after() | 在选定元素后插入内容 |
before() | 在选定元素之前插入内容 |
而用于删除元素的方法有:
方法 | 描述 |
remove() | 删除被选元素容,包括子元素 |
empty() | 删除被选元素的所有子节点和内容 |
append() 方法可以在指定元素的末尾插入内容。
语法如下:
$(selector).append(content,function(index,html))
我们来看下面这个例子:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>jQuery_侠课岛(9xkd.com)</title>
<script src="jquery-3.5.1.min.js"></script>
<script>
$(function(){
$("button").click(function(){
$("p").append("侠课岛");
});
});
</script>
</head>
<body>
<p>你好,我的名字叫做:</p>
<button>点击追加文本</button>
</body>
</html>
点击按钮,在指定的 <p> 标签末尾添加文本内容 “侠课岛”,我们可以在浏览器中看一下演示结果:
除了文本内容,我们还可以在元素中添加 HTML :
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>jQuery_侠课岛(9xkd.com)</title>
<script src="jquery-3.5.1.min.js"></script>
<script>
$(function(){
$("button").click(function(){
$("ul").append("<li>strawberry</li>");
});
});
</script>
</head>
<body>
<ul>
<li>apple</li>
<li>pear</li>
<li>peach</li>
<li>watermelon</li>
</ul>
<button>点击追加文本</button>
</body>
</html>
在浏览器中的演示结果:
prepend() 方法其实和 append() 方法类似,语法也差不多。但是 prepend() 方法主要用于在被选元素的开头插入指定内容。
语法如下:
$(selector).prepend(content,function(index,html))
我们将上述示例中的 append() 方法改成 prepend() 方法:
$(function(){
$("button").click(function(){
$("ul").prepend("<li>strawberry</li>");
});
});
然后看一下在浏览器中的演示结果:
after() 方法用于在被选元素后插入指定的内容。看起来 after() 方法和 append() 方法的作用好像差不多,但是其实两个方法还是有区别的。 append() 方法是在被选元素的结尾插入内容,插入的内容仍然在元素内部。而 after() 插入的内容会重新起一行,与被选择的元素并没有什么逻辑上的联系。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>jQuery_侠课岛(9xkd.com)</title>
<script src="jquery-3.5.1.min.js"></script>
<script>
$(function(){
$("button").click(function(){
$("p").after("<p>侠课岛</p>");
});
});
</script>
</head>
<body>
<p>你好,我的名字叫做:</p>
<button>点击追加文本</button>
</body>
</html>
在浏览器中的演示效果:
before() 方法用于在被选元素之前插入指定的内容。它和 prepend() 方法的区别在于一个在被选元素内插入内容,一个在被选元素外。
注意 before() 方法和 after() 方法都是在被选元素外插入内容。append() 和 prepend() 方法都是在被选元素内插入内容。
例如将上述示例中的方法改为 before:
$(function(){
$("button").click(function(){
$("p").before("<p>侠课岛</p>");
});
});
在浏览器中的演示效果:
remove() 方法用于删除被选元素及其子元素。该方法也会删除被选元素的数据和事件。
例如下面这个例子:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>jQuery_侠课岛(9xkd.com)</title>
<script src="jquery-3.5.1.min.js"></script>
<script>
$(function(){
$("button").click(function(){
$("p").remove();
});
});
</script>
</head>
<body>
<p>你好,欢迎来到侠课岛!</p>
<button>点击删除</button>
</body>
</html>
在浏览器中的演示效果:
从上图中可以看到,remove() 方法将指定的 p 元素连标签带元素全部删除。
empty() 方法用于删除被选元素的所有子节点和内容。该方法不会移除元素本身,或它的属性。
我们讲上述示例中的 remove() 方法改为empty() 方法,看看有什么不同:
$(function(){
$("button").click(function(){
$("p").empty();
});
});
在浏览器中的演示效果:
可以看到 empty() 方法只会删除指定元素中的内容,不会删除元素本身,当然如果元素上有属性,属性也不会被删除,大家可以自己试一下。
源:https://www.cnblogs.com/VanFan/p/15685910.html
最近有个需求大致的背景类似:
我已经通过一系列的操作拿到一批学生的考试成绩数据,现在需要筛选成绩大于 95 分的学生名单。
善于写 bug 的我,三下五除二完成了代码的编写:
@Test
public void shouldCompile() {
for (int i = 0; i < studentDomains.size(); i++) {
if (studentDomains.get(i).getScore() < 95.0) {
studentDomains.remove(studentDomains.get(i));
}
}
System.out.println(studentDomains);
}
测试数据中四个学生,成功筛选出了两个 95 分以上的学生,测试成功,打卡下班。
[StudentDomain{id=1, name='李四', subject='科学', score=95.0, classNum='一班'}, StudentDomain{id=1, name='王六', subject='科学', score=100.0, classNum='一班'}]
从业 X 年的直觉告诉我,事情没这么简单。
但是自测明明没问题,难道写法有问题?那我换个写法(增强的 for 循环):
@Test
public void commonError() {
for (StudentDomain student : studentDomains) {
if (student.getScore() < 95.0) {
studentDomains.remove(student);
}
}
System.out.println(studentDomains);
}
好家伙,这一试不得了,直接报错:ConcurrentModificationException。
为了判断普通 for 循环是否有问题,我将原代码加了执行次数的打印:
@Test
public void shouldCompile() {
System.out.println("studentDomains.size():" + studentDomains.size());
int index = 0;
for (int i = 0; i < studentDomains.size(); i++) {
index ++;
if (studentDomains.get(i).getScore() < 95.0) {
studentDomains.remove(studentDomains.get(i));
}
}
System.out.println(studentDomains);
System.out.println("执行次数:" + index);
}
这一加不得了,我的 studentDomains.size() 明明等于 4,怎么循环体内只执行了 2 次。
更巧合的是:执行的两次循环的数据,刚好都符合我的筛选条件,故会让我错以为【需求已完成】。
一个个分析,我们先看为什么普通 for 循环比我们预计的执行次数要少。
这个原因其实稍微有点儿开发经验的人应该都知道:在循环中删除元素后,List 的索引会自动变化,List.size() 获取到的 List 长度也会实时更新,所以会造成漏掉被删除元素后一个索引的元素。
比如:循环到第 1 个元素时你把它删了,那么第二次循环本应访问第 2 个元素,但这时实际上访问到的是原来 List 的第 3 个元素,因为第 1 个元素被删除了,原来的第 3 个元素变成了现在的第 2 个元素,这就造成了元素的遗漏。
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
只要不为空,程序的执行路径会走到 else 路径下,最终调用 fastRemove() 方法:
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index, numMoved);
elementData[--size] = null;
}
在 fastRemove() 方法中,看到第 2 行【把 modCount 变量的值加 1】。
通过编译代码可以看到:增强 for 循环在实际执行时,其实使用的是Iterator,使用的核心方法是 hasnext() 和 next()。
而 next() 方法调用了 checkForComodification():
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
看到 throw new ConcurrentModificationException() 那么就可以结案了:
因为上面的 remove() 方法修改了 modCount 的值,所以这里肯定会抛出异常。
既然知道了普通 for 循环和增强 for 循环都不能用的原因,那么我们先从这两个地方入手。
我们知道使用普通 for 循环有问题的原因是因为数组坐标发生了变化,而我们仍使用原坐标进行操作。
@Test
public void forModifyIndex() {
for (int i = 0; i < studentDomains.size(); i++) {
StudentDomain item = studentDomains.get(i);
if (item.getScore() < 95.0) {
studentDomains.remove(i);
// 关键是这里:移除元素同时变更坐标
i = i - 1;
}
}
System.out.println(studentDomains);
}
采用倒序的方式可以不用变更坐标,因为:后一个元素被移除的话,前一个元素的坐标是不受影响的,不会导致跳过某个元素。
@Test
public void forOptimization() {
List<StudentDomain> studentDomains = genData();
for (int i = studentDomains.size() - 1; i >= 0; i--) {
StudentDomain item = studentDomains.get(i);
if (item.getScore() < 95.0) {
studentDomains.remove(i);
}
}
System.out.println(studentDomains);
}
@Test
public void iteratorRemove() {
Iterator<StudentDomain> iterator = studentDomains.iterator();
while (iterator.hasNext()) {
StudentDomain student = iterator.next();
if (student.getScore() < 95.0) {
iterator.remove();
}
}
System.out.println(studentDomains);
}
你肯定有疑问,为什么迭代器的 remove() 方法就可以呢,同样的,我们来看看源码:
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
我们可以看到:每次执行 remove() 方法的时候,都会将 modCount 的值赋值给 expectedModCount,这样 2 个变量就相等了。
了解 Stream 的童鞋应该都能想到该方法,这里就不过多赘述了。
@Test
public void streamFilter() {
List<StudentDomain> studentDomains = genData();
studentDomains = studentDomains.stream().filter(student -> student.getScore() >= 95.0).collect(Collectors.toList());
System.out.println(studentDomains);
}
在 JDK1.8 中,Collection 以及其子类新加入了 removeIf() 方法,作用是按照一定规则过滤集合中的元素。
@Test
public void removeIf() {
List<StudentDomain> studentDomains = genData();
studentDomains.removeIf(student -> student.getScore() < 95.0);
System.out.println(studentDomains);
}
看下 removeIf() 方法的源码,会发现其实底层也是用的 Iterator 的remove() 方法:
default boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
boolean removed = false;
final Iterator<E> each = iterator();
while (each.hasNext()) {
if (filter.test(each.next())) {
each.remove();
removed = true;
}
}
return removed;
}
详细认真的看完本文的话,最大感悟应该是:还是源码靠谱!
其实在刚从事 Java 开发的时候,这个问题就困扰过我,当时只想着解决问题,所以采用了很笨的方式:
新建一个新的 List,遍历老的 List ,将满足条件的元素放到新的元素中,这样的话,最后也完成了当时的任务。
现在想一想,几年前,如果就像现在一样,抽空好好想想为什么不能直接 remove() ,多问几个为什么,估计自己会比现在优秀很多吧。
当然,只要意识到这个,什么时候都不算晚,共勉!
https://github.com/vanDusty/JDK/tree/master/code-optimization
信息爆炸的互联网时代,网络爬虫如同一把神奇的钥匙,帮助我们打开海量网页内容的大门。然而,在实际操作过程中,不规范的网页格式、纷繁复杂的干扰元素,特别是那些占据屏幕空间、影响阅读体验的广告,往往成为获取高质量数据的一大阻碍。因此,一款专为网络爬虫设计的HTML广告移除神器显得尤为重要。这款工具利用强大的HtmlAgilityPack库,能够迅速而精准地识别并剔除带有class='ad'属性的广告标签,让抓取到的页面内容回归其最纯粹的本质。
代码执行效果如图:
调用代码:
// 假设这是从某个网页上抓取的包含广告的“混乱”HTML文本
string clutteredHtml = @"<html><head><title>网页标题</title></head><body><div class='header'><h1>网站标题</h1></div><div class='nav'><ul><li><a href='#'>首页</a></li><li><a href='#'>关于我们</a></li><li><a href='#'>联系我们</a></li></ul></div><div class='content'><p>正文内容1...</p><p>正文内容2...</p><p>正文内容3...</p></div><div class='ad'>广告1...</div><div class='ad'>广告2...</div><div class='ad'>广告3...</div><div class='footer'><p>© 2023 版权所有</p></div></body></html>";
// 使用广告移除功能对抓取的“脏乱差”HTML进行深度清理
string polishedHtml = ScrubAndRemoveAds(clutteredHtml);
// 广告移除及HTML内容净化的具体实现方法
public static string ScrubAndRemoveAds(string messyHtmlContent)
{
// 创建一个可以解析和理解HTML结构的对象,并载入抓取的HTML文本
var htmlParser = new HtmlDocument();
htmlParser.LoadHtml(messyHtmlContent);
// 扫描整个HTML文档,找到所有标记为广告(class属性值为"ad")的部分并删除
foreach (var adElement in htmlParser.DocumentNode.SelectNodes("//div[@class='ad']"))
{
adElement.Remove(); // 删除广告区域
}
// 返回已经清除广告后的清爽HTML文本
return htmlParser.DocumentNode.OuterHtml;
}
这个代码有效地解决了网络爬虫在抓取数据时遇到的广告难题。无论对于追求极致阅读体验的个人用户,还是力求优化数据质量、节省资源成本的企业级用户,这个小工具都展现出了卓越的价值。无需繁琐的操作流程,一键即可轻松摆脱广告干扰,让你获得高质量、纯净的网页内容。无论是单独处理单个网页,还是批量清洗大量的抓取数据,此工具都能得心应手,为您提供高效便捷的网络数据整理解决方案。朋友们,喜欢就拿去吧,别忘记关注我:代码领域的诗人XY,我是一个乐于分享的人。乐于将自己的知识和经验分享给朋友们,帮助你们解决问题,启发你们的思考。我相信,只有通过分享和交流,我们才能不断进步,才能不断创新。
*请认真填写需求信息,我们会在24小时内与您取得联系。