diff --git a/src/main/java/org/olat/core/gui/components/AbstractComponent.java b/src/main/java/org/olat/core/gui/components/AbstractComponent.java
index d5d4179d613d53374426246b159cbd966c9592ac..77eaf2d7fe504a49eef05d35f668a6d1dc9dd9c0 100644
--- a/src/main/java/org/olat/core/gui/components/AbstractComponent.java
+++ b/src/main/java/org/olat/core/gui/components/AbstractComponent.java
@@ -64,6 +64,7 @@ public abstract class AbstractComponent implements Component {
 	// true when contents have changed since last rendering
 	private boolean dirty = false;
 	private boolean domReplaceable = true;
+	private boolean domReplacementWrapperRequired = true;
 
 	private final List<ComponentEventListener> listeners;
 	private Translator translator;
@@ -410,6 +411,18 @@ public abstract class AbstractComponent implements Component {
 		return this.spanReplaceable;
 	}
 	
+	/**
+	 * @return true: component does not print DOM ID on element; false:
+	 *         component always outputs an element with the dispatch ID as DOM
+	 *         ID
+	 */
+	public boolean isDomReplacementWrapperRequired() {
+		return this.domReplacementWrapperRequired;
+	}
+	public void setDomReplacementWrapperRequired(boolean domReplacementWrapperRequired) {
+		this.domReplacementWrapperRequired = domReplacementWrapperRequired;
+	}
+
 	/**
 	 * to be called only by the container when a child is added
 	 * @param parent
diff --git a/src/main/java/org/olat/core/gui/components/Component.java b/src/main/java/org/olat/core/gui/components/Component.java
index b96f114a43e2b66eb3b7bfc3c141ee9ae7b370d2..a60e5eee07c5509c96f501db2b8d3d249cda12b2 100644
--- a/src/main/java/org/olat/core/gui/components/Component.java
+++ b/src/main/java/org/olat/core/gui/components/Component.java
@@ -92,4 +92,11 @@ public interface Component {
 	
 	public Event getAndClearLatestFiredEvent();
 
+	/**
+	 * @return true: component does not print DOM ID on element; false:
+	 *         component always outputs an element with the dispatch ID as DOM
+	 *         ID
+	 */
+	public boolean isDomReplacementWrapperRequired();
+
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/core/gui/components/link/Link.java b/src/main/java/org/olat/core/gui/components/link/Link.java
index 28451b0a7c2ae1ca19d91c54fbb478f78fe37626..41d32a28a6d616b0d7bf44dd0a7d99af3f74a599 100644
--- a/src/main/java/org/olat/core/gui/components/link/Link.java
+++ b/src/main/java/org/olat/core/gui/components/link/Link.java
@@ -153,10 +153,12 @@ public class Link extends AbstractComponent {
 					+ " component: " + getComponentName() 
 					+ " dispatchId: " + getDispatchID());
 		}
-		setElementId("o_lnk"+getDispatchID());
 		// use span wrappers - if the custom layout needs div wrappers this flag has
 		// to be set manually
 		setSpanAsDomReplaceable(true);
+		// Directly use the dispatch ID for DOM replacement to minimize DOM tree
+		setElementId(getDispatchID());
+		setDomReplacementWrapperRequired(false);
 	}
 	
 	/**
diff --git a/src/main/java/org/olat/core/gui/components/link/LinkRenderer.java b/src/main/java/org/olat/core/gui/components/link/LinkRenderer.java
index fbaff504acfbac8443ee1c73b36bba4be0ffec58..a050eb7cdd439a789e599d3a808817fd48ce553a 100644
--- a/src/main/java/org/olat/core/gui/components/link/LinkRenderer.java
+++ b/src/main/java/org/olat/core/gui/components/link/LinkRenderer.java
@@ -252,6 +252,8 @@ public class LinkRenderer extends DefaultComponentRenderer {
 				text = translator.translate(i18n);
 			}
 			sb.append("<span ");
+			if (elementId != null) sb.append(" id=\"").append(elementId).append("\" ");
+			
 			String description = link.getTextReasonForDisabling();
 			// fallback to title
 			if (description == null) description = link.getTitle();
diff --git a/src/main/java/org/olat/core/gui/control/generic/closablewrapper/CloseableCalloutWindowController.java b/src/main/java/org/olat/core/gui/control/generic/closablewrapper/CloseableCalloutWindowController.java
index 03ad6ec8d1d79e44abc21574bcd6af067c02a776..bac816bab9d8d05ca0467c9973e5a1ce6fc74e3c 100644
--- a/src/main/java/org/olat/core/gui/control/generic/closablewrapper/CloseableCalloutWindowController.java
+++ b/src/main/java/org/olat/core/gui/control/generic/closablewrapper/CloseableCalloutWindowController.java
@@ -137,8 +137,7 @@ public class CloseableCalloutWindowController extends BasicController {
 	public CloseableCalloutWindowController(UserRequest ureq,
 			WindowControl wControl, Component calloutWindowContent,
 			Link targetLink, String title, boolean closable, String cssClasses) {
-		this(ureq, wControl, calloutWindowContent, "o_lnk"
-				+ targetLink.getDispatchID(), title, closable, cssClasses);
+		this(ureq, wControl, calloutWindowContent, targetLink.getDispatchID(), title, closable, cssClasses);
 	}
 
 	/**
diff --git a/src/main/java/org/olat/core/gui/render/Renderer.java b/src/main/java/org/olat/core/gui/render/Renderer.java
index e9ef3619a808c49d989e6fa2778cabc929db8e58..0f2c2846c28787f57d1bc14c1a466cefb24b7a31 100644
--- a/src/main/java/org/olat/core/gui/render/Renderer.java
+++ b/src/main/java/org/olat/core/gui/render/Renderer.java
@@ -201,7 +201,8 @@ public class Renderer {
 		// wrap with div's so javascript can replace this component by doing a document.getElementById(cid).innerHTML and so on.
 		boolean domReplaceable = source.isDomReplaceable();
 		boolean useSpan = source.getSpanAsDomReplaceable();
-		boolean forceDebugDivs = gset.isIdDivsForced();
+		boolean domReplacementWrapperRequired = source.isDomReplacementWrapperRequired();
+		boolean forceDebugDivs = gset.isIdDivsForced();		
 
 		if (source.isVisible()) {
 			int lev = renderResult.getNestedLevel();
@@ -210,7 +211,7 @@ public class Renderer {
 			
 			// for ajax mode: render surrounding divs or spans as a positional
 			// identifier for dom replacement
-			if (domReplaceable && (ajaxon || forceDebugDivs)) {
+			if (domReplaceable && domReplacementWrapperRequired && (ajaxon || forceDebugDivs)) {
 				if (useSpan) {
 					sb.append("<span id=\"o_c").append(source.getDispatchID()).append("\">");
 				} else {
@@ -272,7 +273,7 @@ public class Renderer {
 			
 			// close div for the javascript dom replacement
 			
-			if (ajaxon && domReplaceable) {
+			if (ajaxon && domReplaceable && domReplacementWrapperRequired) {
 				if(useSpan){
 					sb.append("</span>");
 				} else{