admin管理员组文章数量:1124550
I have doubts while creating a custom Gutenberg block are we allowed to directly manipulate DOM? Below is code that I have written in view.js file I want to know how much portion of this can be moved to edit.js or save.js files?
/**
* FrontEnd scripts for the example block.
*/
/**
* Internal dependencies
*/
import './style.scss';
/**
* Get all the headings and create a table of content.
*/
const headings = document.querySelectorAll(
'.entry-content :is(h1, h2, h3, h4, h5, h6)'
);
/**
* Create a table of content.
* Make toggle for display less than 768px
*/
document.addEventListener('DOMContentLoaded', function () {
const tocContainer = document.querySelector(
'.wp-block-rt-blocks-table-of-contents'
);
if (768 > window.innerWidth) {
const originalSvgString = `
<svg width="18" height="13" viewBox="0 0 18 13" fill="none" xmlns=";>
<rect width="18" height="1.21935" rx="0.609677" fill="#AB3A6C"/>
<rect y="11.3789" width="18" height="1.21935" rx="0.609677" fill="#AB3A6C"/>
<rect y="5.68945" width="18" height="1.21935" rx="0.609677" fill="#AB3A6C"/>
</svg>
`;
const alternateSvgString = `
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns=";>
<rect y="16.8579" width="23.8408" height="1.61502" rx="0.807512" transform="rotate(-45 0 16.8579)" fill="#AB3A6C"/>
<rect width="23.8408" height="1.61502" rx="0.807512" transform="matrix(-0.707107 -0.707107 -0.707107 0.707107 18 16.8579)" fill="#AB3A6C"/>
</svg>
`;
const originalImg = new Image();
originalImg.classList.add('original-img');
originalImg.src =
'data:image/svg+xml;base64,' + btoa(originalSvgString);
// Create a new Image element for the alternate SVG
const alternateImg = new Image();
alternateImg.classList.add('alternate-img');
alternateImg.src =
'data:image/svg+xml;base64,' + btoa(alternateSvgString);
let currentState = 0;
/**
* Toggle the SVGs
*/
const toggleSvg = () => {
if (0 === currentState) {
tocContainer.removeChild(originalImg);
// phpcs:ignore --WordPressVIPMinimum.JS.HTMLExecutingFunctions.insertBefore - This is not a security issue.
tocContainer.insertBefore(
alternateImg,
tocContainer.firstChild
);
currentState = 1;
document.querySelectorAll('.toc-item').forEach((item) => {
item.style.display = 'flex';
});
} else {
tocContainer.removeChild(alternateImg);
// phpcs:ignore --WordPressVIPMinimum.JS.HTMLExecutingFunctions.insertBefore - This is not a security issue.
tocContainer.insertBefore(originalImg, tocContainer.firstChild);
currentState = 0;
document.querySelectorAll('.toc-item').forEach((item) => {
item.style.display = 'none';
});
}
};
originalImg.addEventListener('click', toggleSvg);
alternateImg.addEventListener('click', toggleSvg);
// phpcs:ignore --WordPressVIPMinimum.JS.HTMLExecutingFunctions.insertBefore - This is not a security issue.
tocContainer.insertBefore(originalImg, tocContainer.firstChild);
}
const headingsCreated = [];
headings.forEach(function (heading, index) {
const innerText = heading.innerText;
const parentAnchor = document.createElement('a');
parentAnchor.className = 'toc-item';
const div = document.createElement('div');
if ('H1' === heading.tagName) {
div.className = 'heading-h1';
const divForImage = document.createElement('div');
divForImage.className = 'icon';
parentAnchor.appendChild(divForImage);
} else {
div.className = 'heading-h2-h6';
}
const headingId = 'section' + (index + 1);
heading.id = headingId;
parentAnchor.href = '#' + headingId;
div.innerText = innerText;
parentAnchor.appendChild(div);
tocContainer.appendChild(parentAnchor);
headingsCreated.push(parentAnchor);
div.addEventListener('click', function (e) {
e.preventDefault();
const element = document.getElementById(headingId);
const y = element.getBoundingClientRect().top + window.scrollY - 36;
window.scroll({
top: y,
behavior: 'smooth',
});
const activeElement = document.querySelector('.toc-item.active');
if (activeElement) {
activeElement.classList.remove('active');
}
if (null !== parentAnchor.querySelector('.heading-h2-h6')) {
parentAnchor.classList.add('active');
} else {
parentAnchor.classList.add('parent-active');
}
e.preventDefault();
});
});
});
/**
* Check if an element is in the viewport.
* @param {Element} element - The element to check.
* @return {boolean} - True if the element is in the viewport, false otherwise.
*/
function isInViewport(element) {
const rect = element.getBoundingClientRect();
const viewportWidth =
window.innerWidth || document.documentElement.clientWidth;
const viewportHeight =
window.innerHeight || document.documentElement.clientHeight;
return (
50 >= rect.top &&
0 <= rect.left &&
rect.bottom <= viewportHeight &&
rect.right <= viewportWidth
);
}
let previousParentActive = null;
/**
* Add active class to the toc item when the heading is in the viewport.
*/
document.addEventListener('scroll', function () {
headings.forEach(function (heading) {
const headingId = heading.id;
const tocItem = document.querySelector(`[id='${headingId}']`);
if (tocItem) {
if (isInViewport(heading)) {
document.querySelectorAll('.toc-item').forEach((item) => {
if (item.innerText !== tocItem.innerText) {
item.classList.remove('active');
} else {
if (null !== item.querySelector('.heading-h1')) {
if (previousParentActive) {
previousParentActive.classList.remove(
'parent-active'
);
}
item.classList.add('parent-active');
previousParentActive = item;
}
if (null !== item.querySelector('.heading-h2-h6')) {
item.classList.add('active');
}
}
});
}
}
});
});
I have doubts while creating a custom Gutenberg block are we allowed to directly manipulate DOM? Below is code that I have written in view.js file I want to know how much portion of this can be moved to edit.js or save.js files?
/**
* FrontEnd scripts for the example block.
*/
/**
* Internal dependencies
*/
import './style.scss';
/**
* Get all the headings and create a table of content.
*/
const headings = document.querySelectorAll(
'.entry-content :is(h1, h2, h3, h4, h5, h6)'
);
/**
* Create a table of content.
* Make toggle for display less than 768px
*/
document.addEventListener('DOMContentLoaded', function () {
const tocContainer = document.querySelector(
'.wp-block-rt-blocks-table-of-contents'
);
if (768 > window.innerWidth) {
const originalSvgString = `
<svg width="18" height="13" viewBox="0 0 18 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="18" height="1.21935" rx="0.609677" fill="#AB3A6C"/>
<rect y="11.3789" width="18" height="1.21935" rx="0.609677" fill="#AB3A6C"/>
<rect y="5.68945" width="18" height="1.21935" rx="0.609677" fill="#AB3A6C"/>
</svg>
`;
const alternateSvgString = `
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect y="16.8579" width="23.8408" height="1.61502" rx="0.807512" transform="rotate(-45 0 16.8579)" fill="#AB3A6C"/>
<rect width="23.8408" height="1.61502" rx="0.807512" transform="matrix(-0.707107 -0.707107 -0.707107 0.707107 18 16.8579)" fill="#AB3A6C"/>
</svg>
`;
const originalImg = new Image();
originalImg.classList.add('original-img');
originalImg.src =
'data:image/svg+xml;base64,' + btoa(originalSvgString);
// Create a new Image element for the alternate SVG
const alternateImg = new Image();
alternateImg.classList.add('alternate-img');
alternateImg.src =
'data:image/svg+xml;base64,' + btoa(alternateSvgString);
let currentState = 0;
/**
* Toggle the SVGs
*/
const toggleSvg = () => {
if (0 === currentState) {
tocContainer.removeChild(originalImg);
// phpcs:ignore --WordPressVIPMinimum.JS.HTMLExecutingFunctions.insertBefore - This is not a security issue.
tocContainer.insertBefore(
alternateImg,
tocContainer.firstChild
);
currentState = 1;
document.querySelectorAll('.toc-item').forEach((item) => {
item.style.display = 'flex';
});
} else {
tocContainer.removeChild(alternateImg);
// phpcs:ignore --WordPressVIPMinimum.JS.HTMLExecutingFunctions.insertBefore - This is not a security issue.
tocContainer.insertBefore(originalImg, tocContainer.firstChild);
currentState = 0;
document.querySelectorAll('.toc-item').forEach((item) => {
item.style.display = 'none';
});
}
};
originalImg.addEventListener('click', toggleSvg);
alternateImg.addEventListener('click', toggleSvg);
// phpcs:ignore --WordPressVIPMinimum.JS.HTMLExecutingFunctions.insertBefore - This is not a security issue.
tocContainer.insertBefore(originalImg, tocContainer.firstChild);
}
const headingsCreated = [];
headings.forEach(function (heading, index) {
const innerText = heading.innerText;
const parentAnchor = document.createElement('a');
parentAnchor.className = 'toc-item';
const div = document.createElement('div');
if ('H1' === heading.tagName) {
div.className = 'heading-h1';
const divForImage = document.createElement('div');
divForImage.className = 'icon';
parentAnchor.appendChild(divForImage);
} else {
div.className = 'heading-h2-h6';
}
const headingId = 'section' + (index + 1);
heading.id = headingId;
parentAnchor.href = '#' + headingId;
div.innerText = innerText;
parentAnchor.appendChild(div);
tocContainer.appendChild(parentAnchor);
headingsCreated.push(parentAnchor);
div.addEventListener('click', function (e) {
e.preventDefault();
const element = document.getElementById(headingId);
const y = element.getBoundingClientRect().top + window.scrollY - 36;
window.scroll({
top: y,
behavior: 'smooth',
});
const activeElement = document.querySelector('.toc-item.active');
if (activeElement) {
activeElement.classList.remove('active');
}
if (null !== parentAnchor.querySelector('.heading-h2-h6')) {
parentAnchor.classList.add('active');
} else {
parentAnchor.classList.add('parent-active');
}
e.preventDefault();
});
});
});
/**
* Check if an element is in the viewport.
* @param {Element} element - The element to check.
* @return {boolean} - True if the element is in the viewport, false otherwise.
*/
function isInViewport(element) {
const rect = element.getBoundingClientRect();
const viewportWidth =
window.innerWidth || document.documentElement.clientWidth;
const viewportHeight =
window.innerHeight || document.documentElement.clientHeight;
return (
50 >= rect.top &&
0 <= rect.left &&
rect.bottom <= viewportHeight &&
rect.right <= viewportWidth
);
}
let previousParentActive = null;
/**
* Add active class to the toc item when the heading is in the viewport.
*/
document.addEventListener('scroll', function () {
headings.forEach(function (heading) {
const headingId = heading.id;
const tocItem = document.querySelector(`[id='${headingId}']`);
if (tocItem) {
if (isInViewport(heading)) {
document.querySelectorAll('.toc-item').forEach((item) => {
if (item.innerText !== tocItem.innerText) {
item.classList.remove('active');
} else {
if (null !== item.querySelector('.heading-h1')) {
if (previousParentActive) {
previousParentActive.classList.remove(
'parent-active'
);
}
item.classList.add('parent-active');
previousParentActive = item;
}
if (null !== item.querySelector('.heading-h2-h6')) {
item.classList.add('active');
}
}
});
}
}
});
});
Share
Improve this question
asked Mar 6, 2024 at 14:25
up1512001up1512001
236 bronze badges
1 Answer
Reset to default 1I have doubts while creating a custom Gutenberg block are we allowed to directly manipulate DOM?
Yes but not in the way you've done, that's not how React code works. When a react component is no longer shown it ceases to exist, and the associated DOM node vanishes, along with all its event handlers. This is both expected and desirable.
This isn't a WordPress/Gutenberg question, it's more generic than that.
For this reason, using document.querySelector
on DOM nodes created by React is very very risky and fragile. E.g. blocks that aren't on the screen, or hidden in containers won't be found by the query selector in the code in the question, nor will that list update as headings update/change/move. Headings that are offscreen won't even display in the DOM due to virtual list optimisations.
If you're asking so that you can save time and effort learning React then this will not save you time, and it will not be faster/quicker.
Instead, take a look at the terms and conditions block that already exists, and also take a look at block styles and block variants. Writing a new block from scratch is probably unnecessary
I want to know how much portion of this can be moved to edit.js or save.js files?
To save.js
none of it, that would mean the TOC is set in stone and unchangeable in the database until the next time it's saved. This might break your frontend code. Remember, the save component is there to generate a static string of HTML to save in the database, nothing more.
The edit.js
, not much, the approach taken to get a list of headings won't work well in the editor, it would be easier and more reliable to query the WordPress APIs to get this information than to inspect the DOM for it.
Hint: With a null save funciton/component you can render a blocks output in PHP, and even override the output of an existing core block in PHP to print whatever you want. You can even change your edit component to server side rendering to grab the PHP rendered output.
本文标签:
版权声明:本文标题:plugin development - Can we write custom DOM manipulation inside edit.js file while developing custom Gutenberg block? 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736627082a1945697.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论