Оптимизация производительности веб-страницы – как? [закрыто]Javascript

Форум по Javascript
Ответить
Anonymous
 Оптимизация производительности веб-страницы – как? [закрыто]

Сообщение Anonymous »

Ниже приведен код Javascript для страницы поиска по словарю. При вводе буквы «а» во входных данных поиска по глоссарию время взаимодействия с следующей отрисовкой составляет 60 000 мс, что очень медленно и снижает производительность (в пределах 300 записей имеется много совпадений «а»). Как можно изменить приведенный ниже код, чтобы улучшить время отклика? Возможно только ванильное кодирование, поскольку нет доступа к React, SQL и т. д. Все функциональные возможности кода должны быть сохранены. Для информации: «конечная точка const» относится к API списка Sharepoint Classic, а «window.GLOSSARY_ITEMS» — это основной список элементов записи. Код:
document.addEventListener("DOMContentLoaded", function () {
// Search logic
const searchInput = document.getElementById("glossary-search");
const searchButton = document.getElementById("search-query");
const removeAllTagsButton = document.getElementById("removeAllTags");
const categories = document.getElementById("categories");
const container = document.getElementById("entries");
const clearSearchBtn = document.createElement("span");

// Master list of all created entry elements (so we never lose items)
window.GLOSSARY_ITEMS = [];

const endpoint = "URL Sharepoint Classic list API placeholder";
fetch(endpoint, {
method: "GET",
headers: { "Accept": "application/json;odata=verbose" }
})
.then(response => {
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
return response.json();
})
.then(data => {
container.innerHTML = ""; // remove "Loading..." etc
const items = data.d.results;
if (items.length === 0) {
container.innerHTML = "
No results found.
";
return;
}

const latestEntry = items[0]; // first item is the newest because of $orderby=Id desc
const latestTitle = latestEntry.Title
? latestEntry.Title.charAt(0).toUpperCase() + latestEntry.Title.slice(1)
: "No title";
document.getElementById("latest-entry").textContent = latestTitle;

// Build elements and push into master list
items.forEach(item => {
const entryItem = document.createElement("div");
entryItem.classList.add("entry-item");

// --- TAG parsing fix: default to empty string (not "No tags") ---
let tagDisplay = "";
if (item.data_x002d_tags && item.data_x002d_tags.results) {
const numerals = item.data_x002d_tags.results
.map(tag => {
if (typeof tag === "string") return tag.match(/\d+/)?.[0];
if (tag && tag.Label) return tag.Label.match(/\d+/)?.[0];
return null;
})
.filter(val => val !== null);
if (numerals.length > 0) tagDisplay = numerals.join(",");
} else if (typeof item.data_x002d_tags === "string") {
const match = item.data_x002d_tags.match(/\d+/);
if (match) tagDisplay = match[0];
}

entryItem.setAttribute("data-tags", tagDisplay); // empty if none

const title = item.Title ? item.Title.charAt(0).toUpperCase() + item.Title.slice(1) : "No title";
const content = item.entry_x002d_content || "";

entryItem.innerHTML = `
${title}


${content}


`;

// Preprocess content to remove and consolidate spans
const contentDiv = entryItem.querySelector(".entry-content");
preprocessContent(contentDiv);

// build tables (kept identical to your logic)
const tableContainerWrocor = entryItem.querySelector(".entry-table-container-wrocor");
const tableContainerGennot = entryItem.querySelector(".entry-table-container-gennot");

const correctExamples = item["example_x0028_s_x0029__x002d_wro"] || "";
const wrongExamples = item["example_x0028_s_x0029__x002d_cor"] || "";
const correctSentences = correctExamples.split('\n').map(str => str.trim()).filter(Boolean);
const wrongSentences = wrongExamples.split('\n').map(str => str.trim()).filter(Boolean);

if (correctSentences.length > 0 || wrongSentences.length > 0) {
const table = document.createElement("table");
table.classList.add("smooth-table");
const iconRow = document.createElement("tr");
const wrongIconCell = document.createElement("td"); wrongIconCell.classList.add("icon-cell-wrong"); wrongIconCell.innerHTML = ``;
const correctIconCell = document.createElement("td"); correctIconCell.classList.add("icon-cell-correct"); correctIconCell.innerHTML = ``;
iconRow.appendChild(wrongIconCell); iconRow.appendChild(correctIconCell);
table.appendChild(iconRow);
for (let i = 0; i < 12; i++) {
if (correctSentences || wrongSentences) {
const row = document.createElement("tr");
const wrongCell = document.createElement("td"); wrongCell.classList.add("smooth-cell"); wrongCell.innerHTML = `
${wrongSentences || ""}
`;
const correctCell = document.createElement("td"); correctCell.classList.add("smooth-cell"); correctCell.innerHTML = `
${correctSentences || ""}
`;
row.appendChild(wrongCell); row.appendChild(correctCell);
table.appendChild(row);
}
}
tableContainerWrocor.appendChild(table);
} else {
tableContainerWrocor.remove();
}

const genLeft = item["example_x0028_s_x0029__x002d_gen"] || "";
const genRight = item["example_x0028_s_x0029__x002d_gen0"] || "";
const genLeftLines = genLeft.split('\n').map(str => str.trim()).filter(Boolean);
const genRightLines = genRight.split('\n').map(str => str.trim()).filter(Boolean);

if (genLeftLines.length > 0 || genRightLines.length > 0) {
const genTable = document.createElement("table"); genTable.classList.add("smooth-table");
const hasRightColumn = genRightLines.length > 0;
const exampleCount = genLeftLines.length;
let headerText = "Example(s)";
if (exampleCount === 1) headerText = "Example";
else if (exampleCount > 1) headerText = "Examples";

const headerRow = document.createElement("tr");
headerRow.innerHTML = hasRightColumn
? `${headerText}Nederlands`
: `${headerText}`;
genTable.appendChild(headerRow);

for (let i = 0; i < 12; i++) {
if (genLeftLines || genRightLines) {
const row = document.createElement("tr");
const leftCell = document.createElement("td"); leftCell.classList.add("smooth-cell");
if (!hasRightColumn) leftCell.classList.add("full-width-cell");
leftCell.innerHTML = `
${genLeftLines || ""}
`;
row.appendChild(leftCell);
if (hasRightColumn) {
const rightCell = document.createElement("td"); rightCell.classList.add("smooth-cell");
rightCell.innerHTML = `
${genRightLines || ""}
`;
row.appendChild(rightCell);
}
genTable.appendChild(row);
}
}
tableContainerGennot.appendChild(genTable);
} else {
tableContainerGennot.remove();
}

// push to master list (do NOT yet rely on DOM for "truth")
window.GLOSSARY_ITEMS.push(entryItem);
});

// Render all items (from master list) into container
container.innerHTML = "";
window.GLOSSARY_ITEMS.forEach(item => container.appendChild(item));

// After building nodes, add interactivity
initializeEntries();
})
.catch(error => {
container.innerHTML = `
Error fetching data: ${error.message}
`;
console.error("Error fetching data:", error);
});

function preprocessContent(contentDiv) {
// Find all the span elements
const spans = contentDiv.querySelectorAll('span');
// We process the spans in reverse order to avoid issues with collection indexing
for (let i = spans.length - 1; i >= 0; i--) {
const span = spans;

// 1. Define the classes to be stripped (RTE cleanup).
const isRteCleanupSpan = span.classList.contains('NormalTextRun') ||
span.classList.contains('SpellingError') ||
/SCXO\d+|BCX\d+|TextRun|EOP/.test(span.className);

// 2. Define non-content/marker spans to be stripped (Cursor/Paste cleanup).
const isCursorMarkerSpan = (span.children.length === 0 && !span.textContent.trim()) &&
(span.id.startsWith('ms-rterangecursor-') || span.id.startsWith('ms-rterangepaste-'));

if (isCursorMarkerSpan) {
// Just remove the empty marker span without unwrapping any content.
span.remove();
continue; // Move to the next span
}

if (isRteCleanupSpan) {
// Create a temporary element to hold the content to be inserted.
let contentToInsert = document.createElement('span');
contentToInsert.innerHTML = span.innerHTML; // Start with the span's original content

// 1. Check for and apply semantic tags (em, strong, u) based on inline styles.
if (span.style.fontStyle === 'italic') {
// Create an element, move the content into it, and replace the contentToInsert's innerHTML
const em = document.createElement('em');
em.innerHTML = contentToInsert.innerHTML;
contentToInsert.innerHTML = ''; // Clear the temp span
contentToInsert.appendChild(em); // Put the back into the temp span
}
const fontWeight = span.style.fontWeight;
if (fontWeight === 'bold' || parseInt(fontWeight, 10) >= 700) {
// Wrap the content with . We use innerHTML manipulation here for simplicity
// across potentially nested tags.
contentToInsert.innerHTML = '' + contentToInsert.innerHTML + '
';
}
if (span.style.textDecoration && span.style.textDecoration.includes('underline')) {
// Wrap the content with
contentToInsert.innerHTML = '' + contentToInsert.innerHTML + '
';
}

// 2. Unwrap the content of the span: Move the children of the *temporary* content holder
// (which now contains the semantic markup) into the DOM, replacing the original span.
while (contentToInsert.firstChild) {
span.parentNode.insertBefore(contentToInsert.firstChild, span);
}
span.remove();
}
}

contentDiv.innerHTML = contentDiv.innerHTML.replace(/]*>/g, '');
contentDiv.innerHTML = contentDiv.innerHTML.replace(/ |\u00A0/g, ' ');
contentDiv.normalize();
}

// ---------- Helpers ----------
function getItemTags(item) {
return (item.getAttribute('data-tags') || '')
.split(',')
.map(t => t.trim())
.filter(Boolean); // remove empty strings
}

function addStarIcon() {
const entryItems = window.GLOSSARY_ITEMS || [];
entryItems.forEach(item => {
const entryTitle = item.querySelector('.entry-title');
const tagsData = getItemTags(item);

if (!entryTitle.querySelector('.entry-icon')) {
const iconWrapper = document.createElement('span');
iconWrapper.classList.add('entry-icon');
iconWrapper.style.display = "inline-block";
iconWrapper.style.width = "1em";
iconWrapper.style.marginRight = "10px";
entryTitle.prepend(iconWrapper);
}
const iconWrapper = entryTitle.querySelector('.entry-icon');
iconWrapper.innerHTML = tagsData.includes("10") ? '' : '';
});
}

// -------
function initializeEntries() {
const entryItems = (window.GLOSSARY_ITEMS || []).slice();

// Sort and render: stars on top, then alpha
const sortedItems = entryItems.sort((a, b) => {
const aTags = getItemTags(a); const bTags = getItemTags(b);
const aHas10 = aTags.includes("10"); const bHas10 = bTags.includes("10");
if (aHas10 && !bHas10) return -1;
if (!aHas10 && bHas10) return 1;
const titleA = a.querySelector('.entry-title')?.textContent.toLowerCase() || "";
const titleB = b.querySelector('.entry-title')?.textContent.toLowerCase() || "";
return titleA.localeCompare(titleB);
});

container.innerHTML = "";
sortedItems.forEach(item => container.appendChild(item));

sortedItems.forEach(item => {
const entryBody = item.querySelector('.entry-body');
const tagsContainer = item.querySelector('.entry-tags');
const TAGS_FIXED_HEIGHT = 25;

// ensure we don't accidentally attach duplicate click listeners:
if (!item._clickHandlerAdded) {
item.addEventListener('click', (event) => {
const selection = window.getSelection();
if (selection && selection.toString()) return;
const isActive = item.classList.contains('active');
if (isActive) collapseEntry(item, entryBody);
else expandEntry(item, entryBody, tagsContainer, TAGS_FIXED_HEIGHT);
if (selection) selection.removeAllRanges();
});
item._clickHandlerAdded = true;
}

// set tags text visible inside expanded area
const tagsData = getItemTags(item);
const tagNames = tagsData
.map(tagId => {
const option = document.querySelector(`#categories option[value="${tagId}"]`);
return option ? option.textContent.trim() : null;
})
.filter(Boolean)
.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }));

if (tagNames.length > 0 && tagsContainer) {
tagsContainer.innerHTML = 'Tag(s): ' + tagNames.join(', ');
} else if (tagsContainer) {
tagsContainer.innerHTML = '';
}
});

function expandEntry(item, entryBody, tagsContainer, tagsHeight) {
item.classList.add('active');
if (entryBody) {
const realHeight = entryBody.scrollHeight + tagsHeight;
entryBody.style.maxHeight = realHeight + 'px';
entryBody.addEventListener('transitionend', function handler(e) {
if (e.target !== entryBody) return;
if (item.classList.contains('active')) {
entryBody.style.maxHeight = 'none';
}
entryBody.removeEventListener('transitionend', handler);
}, { once: true });
}
if (tagsContainer) { tagsContainer.style.opacity = '1'; tagsContainer.style.visibility = 'visible'; }
}

function collapseEntry(item, entryBody) {
item.classList.remove('active');
if (entryBody) {
entryBody.style.maxHeight = entryBody.scrollHeight + 'px';
requestAnimationFrame(() => { entryBody.style.maxHeight = '0'; });
}
}

function refreshExpandedEntries() {
document.querySelectorAll('.entry-item.active').forEach(item => {
const entryBody = item.querySelector('.entry-body');
const tagsContainer = item.querySelector('.entry-tags');
if (entryBody) {
entryBody.style.maxHeight = 'none';
const TAGS_FIXED_HEIGHT = 20;
const realHeight = entryBody.scrollHeight + TAGS_FIXED_HEIGHT;
entryBody.style.maxHeight = realHeight + 'px';
}
if (tagsContainer) { tagsContainer.style.opacity = '1'; tagsContainer.style.visibility = 'visible'; }
});
}

window.addEventListener('resize', () => {
requestAnimationFrame(() => { setTimeout(refreshExpandedEntries, 50); });
});

document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') {
requestAnimationFrame(() => { setTimeout(() => { requestAnimationFrame(refreshExpandedEntries); }, 200); });
}
});

addStarIcon();
}

// ---------- New helper: remove marks and normalize to merge adjacent text nodes ----------
function removeMarksAndNormalize(root) {
if (!root) return;
// Replace all with their children
const marks = Array.from(root.querySelectorAll('mark'));
marks.forEach(m => m.replaceWith(...Array.from(m.childNodes)));
// Merge adjacent text nodes so multi-character matches work again
// Normalize on the nearest block ancestor (the entry item) to limit scope
if (root.normalize) root.normalize();
// Also normalize parent nodes up one level to be safe (e.g. title's parent)
if (root.parentNode && root.parentNode.normalize) root.parentNode.normalize();
}

function resetEntries(preserveSearch = false) {
const entryItems = (window.GLOSSARY_ITEMS || []).slice();

// Always strip highlights, even if preserveSearch is true, and normalize text nodes
entryItems.forEach(entry => {
removeMarksAndNormalize(entry);
});

// Sort default: star entries on top, then title alphabetical
const sortedItems = entryItems.sort((a, b) => {
const aTags = getItemTags(a); const bTags = getItemTags(b);
const aStar = aTags.includes("10"); const bStar = bTags.includes("10");
if (aStar && !bStar) return -1;
if (!aStar && bStar) return 1;
const aTitle = a.querySelector(".entry-title")?.textContent.toLowerCase() || "";
const bTitle = b.querySelector(".entry-title")?.textContent.toLowerCase() || "";
return aTitle.localeCompare(bTitle);
});

container.innerHTML = "";
sortedItems.forEach(entry => {
entry.style.display = "";
entry.classList.remove("active");
entry.classList.add("collapsed");

const title = entry.querySelector(".entry-title");
const body = entry.querySelector(".entry-body");

if (title && !preserveSearch) {
// reset title to plain text if not preserving search
title.innerHTML = title.textContent;
}

if (body) {
body.style.maxHeight = "0";
}

container.appendChild(entry);
});

addStarIcon();
}

function highlightMatches(root, term) {
if (!root || !term) return;

// Escape regex special characters in term
const escaped = term.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");
const regex = new RegExp(escaped, "gi");

// Collect all text nodes inside element (including nested)
const textNodes = [];
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
acceptNode(node) {
if (
node.parentNode &&
["SCRIPT", "STYLE", "MARK"].includes(node.parentNode.nodeName)
) {
return NodeFilter.FILTER_REJECT;
}
if (node.nodeValue.trim() === "") return NodeFilter.FILTER_SKIP;
return NodeFilter.FILTER_ACCEPT;
},
});
while (walker.nextNode()) textNodes.push(walker.currentNode);

// Combine text from all nodes into one continuous string to detect cross-node matches
let combinedText = "";
const nodeInfo = []; // track which node each range of text belongs to
for (const node of textNodes) {
nodeInfo.push({ node, start: combinedText.length });
combinedText += node.nodeValue;
}

// Find all regex matches in combined string
const matches = [];
let match;
while ((match = regex.exec(combinedText)) !== null) {
matches.push({ start: match.index, end: match.index + match[0].length });
}
if (!matches.length) return;

// Process matches in reverse to avoid messing up text offsets while replacing
matches.reverse().forEach(({ start, end }) => {
// Identify start and end text nodes for this match
const startNode = nodeInfo.findLast((n) => n.start n.start n.node === node).start;
const nodeEnd = nodeStart + node.nodeValue.length;

if (nodeEnd < end) {
// Full node is inside match
frag.append(node.cloneNode(true));
node.nodeValue = ""; // clear original
} else {
// Partial end node
const endOffset = end - nodeStart;
const range2 = document.createRange();
range2.setStart(node, 0);
range2.setEnd(node, endOffset);
frag.append(range2.extractContents());
range2.detach();
break;
}
}

mark.appendChild(frag);
// Insert mark in DOM at original start position
const insertRange = document.createRange();
insertRange.setStart(startNode.node, offset);
insertRange.collapse(true);
insertRange.insertNode(mark);
insertRange.detach();
});
}

function getSelectedTags() {
return Array.from(categories.querySelectorAll("option:checked")).map(opt => opt.value);
}

function searchEntries(suppressAlert = false) {
const searchTermRaw = searchInput.value.trim();
const searchTerm = searchTermRaw.toLowerCase();
const selectedTags = getSelectedTags();
const items = (window.GLOSSARY_ITEMS || []).slice();

items.forEach(item => removeMarksAndNormalize(item));

const hasSearch = !!searchTerm;
const hasTags = selectedTags.length > 0;
let found = false;

if (!hasSearch && !hasTags) {
resetEntries();
clearSearchBtn.style.display = "none";
return;
}

resetEntries(true);

const starTitle = [], normalTitle = [], starBody = [], normalBody = [];

items.forEach(item => {
const titleText = item.querySelector(".entry-title")?.textContent.toLowerCase() || "";
const bodyText = item.querySelector(".entry-body")?.textContent.toLowerCase() || "";
const wrocorText = item.querySelector(".entry-table-container-wrocor")?.textContent.toLowerCase() || "";
const gennotText = item.querySelector(".entry-table-container-gennot")?.textContent.toLowerCase() || "";
const tags = getItemTags(item);
const isStar = tags.includes("10");

const matchesTitle = hasSearch && titleText.includes(searchTerm);
const matchesBody = hasSearch && (bodyText.includes(searchTerm) || wrocorText.includes(searchTerm) || gennotText.includes(searchTerm));
const matchesSearch = matchesTitle || matchesBody;
const matchesTags = hasTags && selectedTags.every(t => tags.includes(t));

if ((matchesSearch && (!hasTags || matchesTags)) || (!hasSearch && matchesTags)) {
found = true;
if (matchesTitle) (isStar ? starTitle : normalTitle).push(item);
else if (matchesBody) (isStar ? starBody : normalBody).push(item);
else normalBody.push(item); // tag-only
} else {
item.style.display = "none";
}
});

if (!found) {
if (!suppressAlert) alert("No search results found.");
resetEntries();
clearSearchBtn.style.display = hasSearch ? "inline-block" : "none";
return;
}

container.innerHTML = "";

// Sort each group alphabetically by title
starTitle.sort((a, b) => {
const titleA = a.querySelector('.entry-title')?.textContent.toLowerCase() || "";
const titleB = b.querySelector('.entry-title')?.textContent.toLowerCase() || "";
return titleA.localeCompare(titleB);
});
normalTitle.sort((a, b) => {
const titleA = a.querySelector('.entry-title')?.textContent.toLowerCase() || "";
const titleB = b.querySelector('.entry-title')?.textContent.toLowerCase() || "";
return titleA.localeCompare(titleB);
});
starBody.sort((a, b) => {
const titleA = a.querySelector('.entry-title')?.textContent.toLowerCase() || "";
const titleB = b.querySelector('.entry-title')?.textContent.toLowerCase() || "";
return titleA.localeCompare(titleB);
});
normalBody.sort((a, b) => {
const titleA = a.querySelector('.entry-title')?.textContent.toLowerCase() || "";
const titleB = b.querySelector('.entry-title')?.textContent.toLowerCase() || "";
return titleA.localeCompare(titleB);
});

// Merge the groups together
let orderedItems = [...starTitle, ...normalTitle, ...starBody, ...normalBody];

// Ensure star-first sorting when only tags are applied
if (!hasSearch) {
orderedItems.sort((a, b) => {
const aTags = getItemTags(a), bTags = getItemTags(b);
const aStar = aTags.includes("10"), bStar = bTags.includes("10");
if (aStar && !bStar) return -1;
if (!aStar && bStar) return 1;
const tA = a.querySelector(".entry-title")?.textContent.toLowerCase() || "";
const tB = b.querySelector(".entry-title")?.textContent.toLowerCase() || "";
return tA.localeCompare(tB);
});
}

orderedItems.forEach(item => {
item.style.display = "";
container.appendChild(item);
if (hasSearch) {
// Pass the raw user term (not lowercased) so highlightMatches uses user's characters (regex is case-insensitive)
expandAndHighlight(item, searchTermRaw);
} else {
item.classList.remove("active");
item.classList.add("collapsed");
}
});
clearSearchBtn.style.display = hasSearch ? "inline-block" : "none";
}

function expandAndHighlight(item, term) {
const titleEl = item.querySelector(".entry-title");
const contentEl = item.querySelector(".entry-content");
const body = item.querySelector(".entry-body");
const wrocor = item.querySelector(".entry-table-container-wrocor");
const gennot = item.querySelector(".entry-table-container-gennot");
const tags = item.querySelector(".entry-tags");

// Just apply highlights (old ones are already cleared in searchEntries)
if (titleEl) highlightMatches(titleEl, term);
if (contentEl) highlightMatches(contentEl, term);
if (wrocor) highlightMatches(wrocor, term);
if (gennot) highlightMatches(gennot, term);

// Expand and compute height with highlights applied
if (body) {
item.classList.add("active");
item.classList.remove("collapsed");

body.style.maxHeight = "none";
requestAnimationFrame(() => {
const TAGS_FIXED_HEIGHT = 20; // keep your constant
const realHeight = body.scrollHeight + TAGS_FIXED_HEIGHT;
body.style.maxHeight = realHeight + "px";
});
} else {
// still mark as active if title-only match
item.classList.add("active");
item.classList.remove("collapsed");
}

if (tags) {
tags.style.opacity = "1";
tags.style.visibility = "visible";
}
}

function checkAndResetEntries() {
const searchVal = searchInput.value.trim();
const hasTags = getSelectedTags().length > 0;
if (!searchVal && !hasTags) resetEntries();
else searchEntries(true);
}

// Event bindings
searchInput.addEventListener("input", () => {
clearSearchBtn.style.display = searchInput.value.trim() ? "block" : "none";
if (!searchInput.value.trim()) checkAndResetEntries();
});

// use keydown rather than keypress for more reliable Enter detection
searchInput.addEventListener("keydown", e => {
if (e.key === "Enter") {
e.preventDefault();
searchEntries(); // rerun search & highlight on Enter
}
});

searchButton.addEventListener("click", () => searchEntries());
// Only reset display if tags changed and no search term
categories.addEventListener("change", () => {
const hasSearch = !!searchInput.value.trim();
const hasTags = getSelectedTags().length > 0;
if (!hasSearch && hasTags) {
// Tag-only filtering, star entries on top
searchEntries(true);
}
if (!hasSearch && !hasTags) {
resetEntries();
}
});

clearSearchBtn.addEventListener("click", () => {
const hadSearch = !!searchInput.value.trim();
searchInput.value = "";
clearSearchBtn.style.display = "none";

// Always remove highlights and normalize
window.GLOSSARY_ITEMS.forEach(entry => removeMarksAndNormalize(entry));

const hasTags = getSelectedTags().length > 0;
if (hadSearch) {
// If tags remain → show tag-only matches; if no tags → reset
if (hasTags) searchEntries(true);
else resetEntries();
} else {
// If no search before, do nothing at all
}
});

}); // DOMContentLoaded end


Подробнее здесь: https://stackoverflow.com/questions/798 ... zation-how
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Javascript»