節我們學習如何使用 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小時內與您取得聯系。