How to react to a specific style attribute change with mutation observers? - javascript

I've been experimenting with Mutation Observers, so far I can apply the relevant functions to react upon adding, removing elements and so on. Now I am wondering, is there a way to target a specific change within a specific style attribute? I know I can observe for attribute changes, but I just don't know how to observe for a specific attribute modification. For example, if the z-index value of #childDiv changes to 5678, modify the style of the parentDiv in order for it to be shown.
<div id="parentDiv" style="display:none;">
<div id="childDiv" style="z-index:1234;">
MyDiv
</div>
</div>

As per the documentation use attributeFilter array and list the HTML attributes, 'style' here:
var observer = new MutationObserver(styleChangedCallback);
observer.observe(document.getElementById('childDiv'), {
attributes: true,
attributeFilter: ['style'],
});
var oldIndex = document.getElementById('childDiv').style.zIndex;
function styleChangedCallback(mutations) {
var newIndex = mutations[0].target.style.zIndex;
if (newIndex !== oldIndex) {
console.log('new:', , 'old:', oldIndex);
}
}

Sorry for offtop.
Conception React is no mutations. If you need to listen some changes of some element (style for example). You can use componentDidUpdate and get element from #refs.parentDiv (set ref before this in render function <div id="parentDiv" ref="parentDiv" style="display:none;">) and after check style and set you z-Index value before new render.

Related

How to add custom attribute for element using vue directives?

I have custom attribute my-custom-attribute which contains the id for the element I need to add and remove this attribute depending on the boolean.
I already tried this code and it is working fine, is there any way to make it using vuejs directives?
HTML:
<div my-custom-attribute="my_element">
...
</div>
JS:
const el = document.getElementById("some_id");
if(my_bool) {
el.setAttribute("my-custom-attribute", "#my-element");
} else {
el.removeAttribute("my-custom-attribute")
}
You can register a directive as global using the below example, it provides you three lifecycle hooks for you to control the behavior, read the following and try to implement. Let us know if any problem occurs with your implementation and start a separate thread
https://v2.vuejs.org/v2/guide/custom-directive.html
Vue.directive('my-custom-directive', {
// When the bound element is inserted into the DOM...
inserted: function (el) {
// Focus the element
el.focus()
}
})

Late "manual" upgrading of an element towards a customized-built-in web component

I have a jQuery plugin (which I don't want to modify) that is dynamically creating a div. Aside from that, I have a webcomponent scrollable-div, which is a customized built-in extended from HTMLDivElement. As I have no control over how that div is created by the jQuery plugin, I need to upgrade it after creation and after it has already been added to the DOM.
class myDiv extends HTMLDivElement {
constructor(...args) {
const self = super(...args);
self.addEventListener('click', (e) => {
e.target.textContent = 'clicked'
})
return self;
}
}
customElements.define('my-div', myDiv, { extends: 'div' });
document.addEventListener('DOMContentLoaded', () => {
// this is where I'm trying to turn the div#upgradeMe into a my-div
upgradeMe.setAttribute('is', 'my-div');
});
<div id="upgradeMe">Click me</div>
Simply adding the is="my-div" attribute obviously does not do the trick, the div simply stays a regular HTMLDivElement. How can I programmatically upgrade a native element that is already in the DOM to a customized built-in web component?
It's not possible because the element is already created as a standard <div> element and not identified when parsed as upgradable (extendable) due to the lack of the is attribute.
If the custom element is already defined, the only possible workaround is to replace the existing by a clone (as suggested in the comments by #barbsan).
The short way:
create a <template> element
copy the div's outerHTML into its innerHTML property
replace the orginal element with the template's content with replaceChild()
class myDiv extends HTMLDivElement {
constructor(...args) {
const self = super(...args);
self.addEventListener('click', (e) => {
e.target.textContent = 'clicked'
})
return self;
}
}
customElements.define('my-div', myDiv, { extends: 'div' });
document.addEventListener('DOMContentLoaded', () => {
// this is where I'm trying to turn the div#upgradeMe into a my-div
upgradeMe.setAttribute('is', 'my-div');
var t = document.createElement( 'template' )
t.innerHTML = upgradeMe.outerHTML
upgradeMe.parentElement.replaceChild( t.content, upgradeMe )
});
<div id="upgradeMe">Click me</div>
Précisions
When an element is parsed, an is value is affected according to the DOM spec:
Elements have an associated namespace, namespace prefix, local name, custom element state, custom element definition, is value. When an element is created, all of these values are initialized.
Only elements with a valid is attribute are identified as customizable:
An element’s custom element state is one of "undefined", "failed", "uncustomized", or "custom". An element whose custom element state is "uncustomized" or "custom" is said to be defined. An element whose custom element state is "custom" is said to be custom.
Therefore if the element has no is attribute at parse time, it will not be customizable. That's why you cannot add the is attribute afterward.
Also in the HTML specs:
After a custom element is created, changing the value of the is attribute does not change the element's behavior, as it is saved on the element as its is value.
The is attribute is used only at element creation (at parse time) to initialize the is value and has no effect if changed when the element is already created. In that sense is value is read-only.
If you want to support all modern browser's you can't customize built in components, Apple said they will never support is="" https://github.com/w3c/webcomponents/issues/509#issuecomment-222860736

Dynamic tabIndex attribute in JSX + React

How would I set the tabIndex attribute on a React component conditionally in the same way, say the disabled attribute is set?
I need to be able to set the value and/or remove the attribute all together.
First try was to make the entire attribute key and value a variable:
<div { tabIndex } ></div>
but the compiler complains.
Second thought was to:
const div;
if( condition ){
div = <div tabIndex="1"></div>
}else{
div = <div></div>
}
However, this is not desirable since my actual components have tons of attributes on them and I'd end up having large amounts of duplicate code.
My only other thought was to use a ref, then use jQuery to set the tabindex attributes, but I would rather not have to do it that way.
Any Ideas?
You can do it using the attribute spread operator:
let props = condition ? {tabIndex: 1} : {};
let div = <div {...props} />
I believe there is a simpler way (than Aaron's suggestion).
React removes an attribute from a JSX element if that attribute's value is null or undefined. I'd need this to be confirmed by someone who knows for sure.
Therefore you can use something like this:
let t1 = condition ? 1 : null;
let div = <div tabIndex={t1}>...</div>;
The tabIndex attribute will be removed if t1 is null.
For example, we have three attributes, tabIndex, class and id.
let tabIndex;
let id;
let className = "tab";
let props = {
tabIndex,
className,
id,
}
let div = <div {...props} />
undefined/null value props will not add in div, so the render result is
<div class="tab" />

How to get external style sheet data for element that has been changed?

I've got an element whose position has been changed via javascript on my page and I need to retrieve the original (external) style for it. What's the best way to accomplish this? I'm assuming there has to be something better than deleting the element and creating a new one so it has default values. JQuery solutions are welcome.
Thanks!
Jquery.Css method injects "style" attribute to the modified element and that overrides all previous css from external file.
To recover default css you can just remove the "style" attribute.
$(selector).removeAttr("style");
Store the original as a data attribute for the element:
$('#element').data('original-css',$('#element').css('top')).css('top','20px');
then to restore:
$('#element').css('top', $('#element').data('original-css'));
So, assuming the element is repositioned by modifying its style directly (e.g. using jQuery's .css() method), you can do this:
// De-jQuerify the element. We need to get the style directly
var element = element.length ? element[0] : element;
// Store the currently-defined positioning that may be overriding
// the stylesheet.
var cur = {top: element.style.top, left: element.style.left};
// Remove any overriding positioning
element.style.left = element.style.top = null;
// Find the position of the element.
var pos = $(element).offset(); // Or $(element).position()
// Restore the old styles
$(element).css(cur);
This also ought to work correctly for elements that don't have their position overridden.

javascript get child by id

<div onclick="test(this)">
Test
<div id="child">child</div>
</div>
I want to change the style of the child div when the parent div is clicked. How do I reference it? I would like to be able to reference it by ID as the the html in the parent div could change and the child won't be the first child etc.
function test(el){
el.childNode["child"].style.display = "none";
}
Something like that, where I can reference the child node by id and set the style of it.
Thanks.
EDIT: Point taken with IDs needing to be unique. So let me revise my question a little. I would hate to have to create unique IDs for every element that gets added to the page. The parent div is added dynamically. (sort of like a page notes system). And then there is this child div. I would like to be able to do something like this: el.getElementsByName("options").item(0).style.display = "block";
If I replace el with document, it works fine, but it doesn't to every "options" child div on the page. Whereas, I want to be able to click the parent div, and have the child div do something (like go away for example).
If I have to dynamically create a million (exaggerated) div IDs, I will, but I would rather not. Any ideas?
In modern browsers (IE8, Firefox, Chrome, Opera, Safari) you can use querySelector():
function test(el){
el.querySelector("#child").style.display = "none";
}
For older browsers (<=IE7), you would have to use some sort of library, such as Sizzle or a framework, such as jQuery, to work with selectors.
As mentioned, IDs are supposed to be unique within a document, so it's easiest to just use document.getElementById("child").
This works well:
function test(el){
el.childNodes.item("child").style.display = "none";
}
If the argument of item() function is an integer, the function will treat it as an index. If the argument is a string, then the function searches for name or ID of element.
If the child is always going to be a specific tag then you could do it like this
function test(el)
{
var children = el.getElementsByTagName('div');// any tag could be used here..
for(var i = 0; i< children.length;i++)
{
if (children[i].getAttribute('id') == 'child') // any attribute could be used here
{
// do what ever you want with the element..
// children[i] holds the element at the moment..
}
}
}
document.getElementById('child') should return you the correct element - remember that id's need to be unique across a document to make it valid anyway.
edit : see this page - ids MUST be unique.
edit edit : alternate way to solve the problem :
<div onclick="test('child1')">
Test
<div id="child1">child</div>
</div>
then you just need the test() function to look up the element by id that you passed in.
If you want to find specific child DOM element use method querySelectorAll
var $form = document.getElementById("contactFrm");
in $form variable we can search which child element we want :)
For more details about how to use querySelectorAll check this page

Categories

Resources