React Hooks - Using useRef without direct access to HTML Element Code? - javascript

With React I'm inside of one repository, and the HTML elements are loading from another repo, which I watch for using pageLoaded. Inside updateHeader there is just more HTML element selecting and attribute/class manipulation.
useEffect(() => {
if (pageLoaded.status) {
if (someCondition) {
updateHeader(ubeName);
} else {
document.querySelector('.headerbar-menu').style.display = 'block';
if (document.querySelector('.headerbar-menu.affiliates-wrapper')) {
document.querySelector('.headerbar-menu.affiliates-wrapper').style.display = 'none';
}
}
}
}, [pageLoaded.status])
The problem here is obviously we shouldn't be using querySelector, and i think it may be causing some unexpected functionality. The elements dont properly evaluate and render until some piece of state changes i.e. a scroll state handler, so on initial page load the elements dont show with their new attributes until a scroll.
I'd like to attempt to resolve using useRef, but don't have direct access to the html. Is there a way to dynamically connect a ref to an element without access to the HTML code?
Thanks for your time and attention!

The best way to do this, in my opinion, is in this sequence:
Attempt to select the element
If non-existant, set up a DOMSubtreeModified event handler or a MutationObserver
Clean up DOMSubtreeModified event handler or a MutationObserver (or keep it around to watch for updates)
This allows for use of query selectors in a safe and modern way which doesn't stray too far from React's recommendations

Related

Adding dynamic eventlistners in VueJS3/Nuxt app

Im currently struggling with something where i want to add a data attribute to a component and then based on when NUXT is loaded have a click event bind to all nodes that have this data attribute. Im not using v-on because i want to have this separated from current Vue logic. So example:
component a.vue is tagged with data-element
<a href="/somelink" data-element>link</a>
component b.vue also has HTM elements tagged with data-element
<button data-element>link</button>
When the app loads i need to then loop through all data-element and bind an eventlistner to them.
I tried the above method and that works to some degree but fails when reactivity sets in and the DOM is updated. I checked mixins (not recommended using VueJS3), used composition API, used a mutation observer that checked the DOM status for changes and based on new elements loaded it ads click events, looked at hooks etc but now im getting confused at what is the best way to proceed. Some solutions work to some extend but feels hacky. Or is there a completely different approach i am missing.
To be Sure that the DOM has been initialized you need to set your event listeners on "mounted" lifecycle
mounted() {
const elements = [...document.querySelectorAll('[data-element]')];
elements.forEach(element => {
element.addEventListener('click', () => { /* your code here. */ })
})
}

Is it good idea in React to modify attribute values of an element through Javascript?

Since React has its own copy of the DOM, what is the best way to modify an HTML element's attributes without affecting React's virtual DOM (Performance)?
Let's say I want to toggle between adding and removing an active class from a list of divs based on their visibility (Intersection Observer)
In Javascript I can just do:
if (inView) {
element.classList.add('active')
} else {
element.classList.remove('active')
}
Will this affect React's virtual DOM? If yes can you please suggest the better way to do it?

How to access element in React JS that render by JS plugin

Is there a way to access the HTML element that rendered by JS plugin in React JS Ecosystem. I can't use ref on those elements.
for example:- If I use React Slick plugin, It adds many HTML elements that can't access by React JS. I need to trigger a mouse hover event on those elements.
Does anyone have a solution for this kind of situation?
Thanks.
Suppose the id of the container div that wraps the <Slider/> component is called container then the following code should work in attaching the mouseover event to all the nodes (divs in the slider):
componentDidMount(){
let nodes = document.getElementsById("container").childNodes[0].childNodes
nodes.forEach(node => {
node.addEventListener('mouseover', () => {
//action to be performed here
}
})
}
maybe it will help
componentdidmount{
const elemnts = [...document.getElemetntByClassName('ClassName')]
elements.forEach(e => e.addEventListener('mouseover', () => {
// your code here
}))
}
Plugins has their own class and id. You can always inspect and customize the class they are using. But this may cause global changes. What you can do is to use custom class and selectors for separate components. Then you can make any changes in the css.

How do you manage an HTML element's focus() and blur() methods with snabbdom?

The title pretty much says it all. With JavaScript and direct DOM element access you can simply do:
domElement.focus()
domElement.blur()
How do you do this through the virtual DOM in snabbdom? I think this is one of those cases in reactjs where you would just open the escape hatch and use refs. Is there something like this available with snabbdom?
Answered my own question.
Snabbdom's lifecycle hooks give you access to a virtual dom element's underlying real dom element via the vnode.elm property.
h('button', {
update: {
update (oldVnode, vnode) { vnode.elm.focus(); }
}
});

How should I bind an event to DOM elements created with a JsRender custom tag?

Right now, I'm binding events to the parent element of my custom tag's rendered content, then using classes to target the event onto the element which my custom tag actually renders. I feel this is likely to cause strange bugs. For instance, if anyone on my team places two custom tags using the same targeting-classes under the same immediate parent element, it would cause multiple events to fire, associated with the wrong elements.
Here's a sample of the code I'm using now:
$.views.tags({
toggleProp: {
template: '<span class="toggle">{{include tmpl=#content/}}</span>',
onAfterLink: function () {
var prop = this.tagCtx.view.data;
$(this.parentElem).on('click', '.toggle', function () {
prop.value(!prop.value());
});
},
onDispose: function () {
$(this.parentElem).off('click', '.toggle');
}
}
// ... other custom tags simply follow the same pattern ...
});
By the time we hit onAfterLink, is there any reliable way to access the rendered DOM Element (or DOM Elements) corresponding to the custom tag itself? With no risk of hitting the wrong element by mistake? I understand that the custom tag may be text without an HTML Element, but it would still be a text node, right? (Could I even bind events to text nodes?)
In other places, and using (far) older versions of JsViews, I've bound events after the render using (sometimes a lot of) targeting logic built into the rendered elements as data- attributes. Not only is this a far more fragile method than I like for accessing the rendered data, it would be incredibly risky and convoluted to try to apply this approach to some of our deeply-nested-and-collection-ridden templates.
I also don't like needing to insert a span with my custom tag, just so I can apply classes to it, but if it's still necessary for the event, I'll cope.
I ask, then, what is a safe, modular way to bind events to the DOM so that I also have access to the data rendered directly against those elements?
Edit: As an additional concern, using onAfterLink won't let me bind events to non-data-linked rendered content. This may be part of the design intent of JsViews vs pure JsRender, but I don't yet understand why that would be the case.
Rather than using this.parentElem, you can use
this.contents()
which is a jQuery object containing all immediate content elements within the tag.
You can also provide a selector argument,
this.contents("someselector")
to "filter" , and include an optional boolean "deep" flag to both "filter" and "find" - i.e.
this.contents("someselector", true).
Using the above APIs ensures you are only taking elements that are actually within the tag content.
You may not need to remove the handlers in onDispose, if the tag is only deleted along with its content, you can rely on the fact that jQuery will dispose handlers when the elements are removed from the DOM.
You can only attach events to elements, not to text nodes. So if your content does not include elements, you would need to add your wrapper element, but not otherwise.
$.views.tags({
toggleProp: {
template: '{{include tmpl=#content/}}',
onAfterLink: function () {
var prop = this.tagCtx.view.data;
this.contents().on('click', function () {
prop.value(!prop.value());
});
},
onDispose: function () {
this.contents().off('click');
}
}
});
Also take a look at samples such as http://www.jsviews.com/#samples/tagcontrols/tabs which use the above approach.

Categories