I'm pretty much new to Svelte framework. Recently I'm playing around with Svelte but confused how I would manipulate the DOM like in jQuery using Svelte.
I'm trying to show/hide an <li> on button click.
In Svelte, and other state-driven UI frameworks, you very rarely manipulate the DOM. Instead, you essentially tell the framework what the DOM should look like for some given data, and let it figure out all the details.
So to show or hide elements in response to a button click, you'd do something like this (REPL link):
<button on:click='set({ visible: !visible })'>toggle visibility</button>
{{#if visible}}
<p>hello!</p>
{{/if}}
Or, if you want to keep the element in the DOM but make it hidden (REPL link):
<button on:click='set({ visible: !visible })'>toggle visibility</button>
<p hidden='{{!visible}}'>hello!</p>
This is much better than manually manipulating the DOM, because you can change all the details — what kind of element it is and therefore what jQuery selector you'd use to target it, whether there are other things that also need to change based on visible, etc — without having to rewrite all your code.
Related
I'm using Angular 6 and Angular Material. I'm attempting to implement a simple drag and drop list inside of a Material tab (). Drag and drop works fine outside of a material tab, but inside the tab it's very slow (18 seconds to DnD 1 item in a list of 5 items). The onDragEvent fires many many times even when the mouse isn't moving. I've tried detaching (onDragStart) and reattaching (onDrop) the change detector. It doesn't make a difference.
Here's the html:
<div (dragover)="onDragOver($event)">
<div *ngFor="let item of listItems, let i = index"
(drop)="onDrop($event, i)"
[draggable]="true"
(dragstart)="onDragStart(i)">
{{item}}
</div>
</div>
Here are the functions:
onDragStart(i) {
}
onDragOver(event) {
console.log('1')
event.preventDefault();
}
onDrop(event, i) {
}
Thank you.
It appears that as I dragged the item around the screen- even briefly, the Angular engine was overfiring and queuing up a ridiculous number of calls of onDrop and/or onDragEnter. And that when angular hooks those events, it also assumes that something in the angular components has changed and it runs through the change detection routines.
Each... time.
What I did that fixed my problem was to turn off change detection when dragging began and turn it back on when dragging was complete.
Here is a brief hint at what I did:
import ChangeDetectorRef from '#angular/core'
inject ChangeDetector into your constructor:
constructor(private cdr: ChangeDetectorRef)
call detach from the dragStart:
this.cdr.detach();
call reattach from the drop and dragEnd:
this.cdr.reattach();
If your component is buried deep on your page, you may have to detach change detection at a higher level in order to see results.
i was experiencing a similar issue, where dragging when there were many elements on the page was unbearably slow. I found that removing the (dragover) angular binding, and replacing it with pure javascript instantly solved the problem
i switched
(dragover)="this.onDragOver($event)"
to
ondragover="onDragOver(event)"
you also need to declare the function in index.html instead of in your typescript file or html template (hence pure javascript)
I am trying to find out if anyone has an approach to automated UI testing on Material UI components.
Material UI elements are rendered as nested divs with very little unique id information, for example:
<div data-reactroot style="...">
<div style="...">
<div style="...">
</div>
</div>
</div>
The nested div structure makes using traditional location methods difficult if not impossible - (Selenium and Watir), id, name, class, etc.
Using react devtools, one can see a much clearer picture of how the page is structured, but I am not yet able to access the React "DOM" to locate elements.
Any ideas or help would be appreciated.
Added example:
Sliders
I can't come up with an example that is more descriptive than the one above, could literally be 10 layers of nested divs without any text.
There is no general method I'm aware of, unfortunately.
Some of the components already have ids, which allows you to use a css selector like #my-component input (which is usually enough to get an exact field), others have custom class names to be added (like AutoComplete - popoverProps) which allows you to use a similar selector.
Good news is that every MaterialUI component provides className, which can be used to locate elements (at least partially) - details can be found at http://www.material-ui.com/#/customization/styles
Also id field works quite often, even when not documented.
At the last resort (if detection by class + other css selector parts is not sufficient) you can fall back to XPath expression using element text - for example, I use //span[#class="menu-item"][.//div[contains(text(),"${itemName}")]] for matching menu items. It matches things declared as <MenuItem primaryText={itemName} className="menu-item">
I would like to ask how I can block richText from changing html text under source view.
I'm using Blossom module and defined richText as #Chris J advised me to do:
Add source button to Magnolia CMS richText control
Whenever I put html code in source code, switch to normal view and get back to source view the code is changed. For example the following part of code is missing :
<div class="components"> <div class="product col img-slider"> <div id="product-image" class="royalSlider productImage rsDefault"> <div class="rsContent"> <div class="rsTmb"><img src="/magnoliaPublic/resources/XXX/products/product_7.jpg" alt="">
and is replaced with folowing
<p><img alt="" src="/magnoliaPublic/resources/XXX/products/product_7.jpg" /></p>
I need to provide the possibility for the user to put html code and next to see in on the web page.
Regards
Jan
Jan. I'd ask why you are using a rich text area if you are entering HTML. It is not really designed for this usage. Would you be better off with an ordinary text field? In the STK (you mentioned this in your previous question) you will find a component that serves exactly this purpose.
Under "Configuration" you will find it at /modules/standard-templating-kit/templates/components/content/stkHTML
You will see that the template script is simply:
[#if content.editHTML?has_content]
${cmsfn.decode(content).editHTML}
[/#if]
If you want to stick with a purely Blossom approach, you may need to recreate this but it is an incredibly simple component.
Incidentally, in Magnolia 5.4 there is a code editing field used in a similar component that offers syntax highlighting. You can see this by logging into the demo site and trying to add an HTML component to the main area of the page travel/contact.
In polymer I'm trying to manually submit a form. My form looks like this:
<form id="myForm" on-submit="{{ submitForm }}">
<input class="text" value="{{ someValue}}">
<button type="submit">Submit</button>
</form>
And in the polymer object I have:
submitForm: function(e) {
e.preventDefault();
}
Whenever I try to do the following:
document.getElementById('myForm').submit();
the form totally ignores the on-submit attribute and posts the form to a new page.
I'm building a on-screen keyboard for anyone wondering why I would want to do this. I need to submit the form whenever someone hits the enter key on the on-screen keyboard.
Does anyone know why this happens?
A JSBin example to show you the exact problem (see the alerts): http://jsbin.com/wadihija/2/
From the MDN page about submit:
The form's onsubmit event handler will not be triggered when invoking this
method ... it is not guaranteed to be invoked by HTML user agents.
However, calling click on a submit type button seems to work. See here:
http://jsbin.com/tuxac/2/edit
Here is a modification of your jsbin that I believe does what you want:
http://jsbin.com/wadihija/6/edit
Is this along the lines of what you're trying to do? This is a result of a key feature of Shadow DOM: Encapsulation. The elements in your polymer-element's template are not in the main document, and as such, are not available via document.getElementById() and the like.
You could instead call this.shadowRoot.getElementById() and it would work because this inside of your polymer-element's prototype is linked to the host element. Or even better, take advantage of the amazing features Polymer gives you for free. Polymer exposes this.$ to polymer-elements, which contains a key for every element in your template that has an ID! No method call needed, just use this.$.myForm.submit(). Here's the final jsbin.
I am using CKEditor in DIV mode, as compared to an IFRAME and I am attempting to assign a class to the editor itself. I have found where to add it to things within the editor, but not the editor itself. And, I would prefer to not wrap the editor within another DIV to get the effect I want.
I am also using version 4 of CKEditor.
Edit: The following was my questions to Reinmar after he suggested the Shared Space plugin, which at least for now I have chosen not to use.
Edit: In response to Reinmar I have begun using the Shared Space plugin, and do see the potential benefits of using it over a DIV.
With that said I have the following code:
<div id="topSpace"></div>
<textarea name="data[ArchiveQuarter][description]" class="userContent" id="editor1" cols="30" rows="6"></textarea>
At the bottom of the page I have:
<script type="text/javascript">
CKEDITOR.disableAutoInline = true;
CKEDITOR.replace( 'editor1', {
extraPlugins: 'sharedspace',
sharedSpaces: {
top: 'topSpace',
}
});
</script>
It currently creates the toolbar within the top space, and has the textarea, but both of them are disabled. I probably just messed up some of the configuration, but I'm not sure what.
I would greatly prefer it to use the textarea configuration as it is part of a form instead of extracting the data from inline.
You might be interested in using Shared space plugin:
addon page,
sample.
I'm proposing this instead of using div, because I've got mixed feelings regarding divarea plugin. Your original container is wrapped with editor's structure what changes the real context. IMO it's better to use real inline editing + the shared spaces feature to place toolbar and bottom bar where you need them.
Update:
When you're using inline editor, you don't need textarea. Textarea is only a data container which framed or div based editors replace with themselves.
Inline editing is all about editing real existing elements. So this can be your HTML:
<div id="topSpace"></div>
<div class="userContent" id="editor1"><h1>My page</h1><p>Fooo!</p></div>
And JS:
<script type="text/javascript">
CKEDITOR.disableAutoInline = true;
CKEDITOR.inline( 'editor1', {
extraPlugins: 'sharedspace',
sharedSpaces: {
top: 'topSpace',
}
} );
</script>
Note that I used CKEDITOR.inline not CKEDITOR.replace.
And the huge advantage of inline editing is that that <div> is a real element on your page - it is not wrapped (as in div based editor) and its contents is not moved to the frame (as in framed editor). So your content will inherit styles of your page.
The downside is that you need to implement custom data saving, because there's no form. The simplest way is to add a "save" button which clicked will send editor.getData() via AJAX to your server.
BTW. You probably was confused by the fact that in the shared spaces sample 2 editors are framed and 2 are inline. All of them reuses one top space and one bottom space.
BTW2. To make use of inline editing you don't need shared spaces in fact. Then the "floating toolbar" will be used as in here: http://ckeditor.com/demo#inline